当前位置: 代码迷 >> C# >> C#多线程技术小结(同步)
  详细解决方案

C#多线程技术小结(同步)

热度:410   发布时间:2016-04-28 08:44:16.0
C#多线程技术总结(同步)

二、串行(同步):

1.lock、Monitor--注意锁定的对象必需是引用类型(string类型除外)

示例:

        private static object syncObject = new object();        private static void TaskWork(object i)        {            Console.WriteLine("我是任务:{0}",i);            lock (syncObject)            {                Thread.Sleep(1000);                Console.WriteLine("我是任务:{0},线程ID:{1}",i,Thread.CurrentThread.ManagedThreadId);            }            try            {                Monitor.Enter(syncObject);                Console.WriteLine("我是任务:{0},线程ID:{1}", i, Thread.CurrentThread.ManagedThreadId);            }            finally            {                Monitor.Exit(syncObject);            }        }//调用Task.Factory.StartNew(TaskWork,1);Task.Factory.StartNew(TaskWork, 2);

2.Interlocked

示例:

            int i=1;            Interlocked.Increment(ref i); //增量+1=2;            Console.WriteLine("i当前的值:{0}", i);            Interlocked.Decrement(ref i); //减量-1=0;            Console.WriteLine("i当前的值:{0}", i);            Interlocked.Exchange(ref i, 2);//赋值=2;            Console.WriteLine("i当前的值:{0}",i);            Interlocked.CompareExchange(ref i, 10, 2);//比较交换值,当i=2时,则将i赋值为10;            Console.WriteLine("i当前的值:{0}", i);

3.Mutex--可以实现进程间的同步,甚至是两个远程进程间的同步

示例:

            var t1 = new Task(() =>            {                Console.WriteLine("我是第一个任务!");                Mutex m = new Mutex(false, "test");                m.WaitOne();                Console.WriteLine("第一个任务完成!");                m.ReleaseMutex();            });            var t2 = new Task(() =>            {                Console.WriteLine("我是第二个任务!");                Mutex m = new Mutex(false, "test");                m.WaitOne();                Console.WriteLine("第二个任务完成!");                m.ReleaseMutex();            });            t1.Start();            t2.Start();

 4.ReaderWriterLock 、ReaderWriterLockSlim--如果在某一时刻资源并没有获取写的独占权,那么可以获得多个读的访问权,单个写入的独占权,如果某一时刻已经获取了写入的独占权,那么其它读取的访问权必须进行等待. 

示例:

        static ReaderWriterLock rwLock = new ReaderWriterLock();        static void Read(object state)        {            Console.WriteLine("我是读线程,线程ID是:{0}",Thread.CurrentThread.ManagedThreadId);            rwLock.AcquireReaderLock(Timeout.Infinite);//无限期等待,需要显式调用ReleaseReaderLock释放锁            var readList = state as IEnumerable<int>;            foreach (int item in readList)            {                Console.WriteLine("读取当前的值为:{0}", item);                Thread.Sleep(500);            }            Console.WriteLine("读完成,线程ID是:{0}", Thread.CurrentThread.ManagedThreadId);            rwLock.ReleaseReaderLock();                    }        static void Write(object state)        {            Console.WriteLine("我是写线程,线程ID是:{0}", Thread.CurrentThread.ManagedThreadId);            rwLock.AcquireWriterLock(Timeout.Infinite); //无限期等待,需要显式调用ReleaseWriterLock释放锁            var writeList = state as List<int>;            int lastCount=writeList.Count();            for (int i = lastCount; i <= 10+lastCount; i++)            {                writeList.Add(i);                Console.WriteLine("写入当前值:{0}",i);                Thread.Sleep(500);            }            Console.WriteLine("写完成,线程ID是:{0}", Thread.CurrentThread.ManagedThreadId);            rwLock.ReleaseWriterLock();        }    //调用:            var rwList = new List<int>();            var t1 = new Thread(Write);            var t2 = new Thread(Read);            var t3 = new Thread(Write);            var t4 = new Thread(Read);                        t1.Start(rwList);            t2.Start(rwList);            t3.Start(rwList);            t4.Start(rwList);

 

5.SynchronizationAttribute--确保某个类的实例在同一时刻只能被一个线程访问,类的定义要求:A.类上必需标记SynchronizationAttribute特性,B.类必需继承自System.ContextBoundObject对象

示例:

    [Synchronization(SynchronizationAttribute.REQUIRED,true)]    public class Account : System.ContextBoundObject    {        private static int _balance;        public int Blance        {            get            {                return _balance;            }        }        public Account()        {            _balance = 1000;        }        public void WithDraw(string name,object money)        {            if ((int)money <= _balance)            {                Thread.Sleep(2000);                _balance = _balance - (int)money;                Console.WriteLine("{0} 取钱成功!余额={1}", name, _balance);            }            else            {                Console.WriteLine("{0} 取钱失败!余额不足!", name);            }        }    }//调用:            var account = new Account();            Parallel.Invoke(() =>            {                account.WithDraw("张三",600);            }, () =>            {                account.WithDraw("李四",600);            });

6.MethodImplAttribute--使整个方法上锁,直到方法返回,才释放锁

示例:

    public class Account    {        private static int _balance;        public int Blance        {            get            {                return _balance;            }        }        public Account()        {            _balance = 1000;        }        [MethodImpl(MethodImplOptions.Synchronized)]        public void WithDraw(string name,object money)        {            if ((int)money <= _balance)            {                Thread.Sleep(2000);                _balance = _balance - (int)money;                Console.WriteLine("{0} 取钱成功!余额={1}", name, _balance);            }            else            {                Console.WriteLine("{0} 取钱失败!余额不足!", name);            }        }    }//调用            var account = new Account();            Parallel.Invoke(() =>            {                account.WithDraw("张三",600);            }, () =>            {                account.WithDraw("李四",600);            });

7.AutoResetEvent、ManualResetEvent、ManualResetEventSlim--调用WaitOne、WaitAny或WaitAll来使线程等待事件,调用Set方法发送信号,事件将变为终止状态,等待的线程被唤醒

示例:

            AutoResetEvent arEvent = new AutoResetEvent(false);//默认为无信号,处于非终止状态            Task.Factory.StartNew((o) => {                for (int i = 1; i <= 10; i++)                {                    Console.WriteLine("循环第{0}次",i);                }                arEvent.Set();//发送信号,处于终止状态            },arEvent);            arEvent.WaitOne();//等待信号,收到信号后则继续下面的执行            Console.WriteLine("我是主线程,我继续执行!");            Console.Read();

 8.Sempaphore、SemaphoreSlim(不可跨进程)--信号量,可实现线程、进程间同步

示例:

    public class WashRoom    {        private readonly Semaphore sem;        public WashRoom(int maxUseableCount)        {            sem = new Semaphore(maxUseableCount, maxUseableCount, "WC");        }        public void Use(int i)        {            Task.Factory.StartNew(() =>                {                    Console.WriteLine("第{0}个人等待进入", i);                    // WaitOne:如果还有“空位”,则占位,如果没有空位,则等待;                    sem.WaitOne();                    Console.WriteLine("第{0}个人成功进入,使用中", i);                    // 模拟线程执行了一些操作                    Thread.Sleep(100);                    Console.WriteLine("第{0}个人用完,离开了", i);                    // Release:释放一个“空位”                    sem.Release();                });        }    }//调用:            var wc = new WashRoom(5);            for (int i = 1; i <= 7; i++)            {                wc.Use(i);            }

9.Barrier--屏障,使多个任务能够采用并行方式依据某种算法在多个阶段中协同工作,即:将一个阶段的事情分成多个线程来异步执行,执行完毕后再同时进入下一个阶段

示例:

            int taskSize = 5;            Barrier barrier = new Barrier(taskSize, (b) =>            {                Console.WriteLine(string.Format("{0}当前阶段编号:{1}{0}", "-".PadRight(15, '-'), b.CurrentPhaseNumber));            });            var tasks = new Task[taskSize];            for (int i = 0; i < taskSize; i++)            {                tasks[i] = Task.Factory.StartNew((n) =>                {                    Console.WriteLine("Task : #{0}   ---->  处理了第一部份数据。", n);                    barrier.SignalAndWait();                    Console.WriteLine("Task : #{0}   ---->  处理了第二部份数据。", n);                    barrier.SignalAndWait();                    Console.WriteLine("Task : #{0}   ---->  处理了第三部份数据。", n);                    barrier.SignalAndWait();                }, i);            }            Task.WaitAll(tasks);

10.SpinLock--自旋锁,仅限锁定的时间较短

示例:

            SpinLock sLock = new SpinLock();            int num = 0;            Action action = () =>            {                bool lockTaken = false;                for (int i = 0; i < 10; i++)                {                    lockTaken = false;                    try                    {                        sLock.Enter(ref lockTaken);                        Console.WriteLine("{0}+1={1} ---线程ID:[{2}]", num, ++num,Thread.CurrentThread.ManagedThreadId);                        Thread.Sleep(new Random().Next(9));                    }                    finally                    {                        //真正获取之后,才释放                        if (lockTaken) sLock.Exit();                    }                }            };//多线程调用:            Parallel.Invoke(action, action, action);            Console.WriteLine("合计:{0}", num);

11.SpinWait--自旋等待,轻量级

            Thread.Sleep(1000);//线程等待1S;            Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"));            SpinWait.SpinUntil(() => false, 1000);//自旋等待1S            Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"));            Thread.SpinWait(100000);//指定CPU的循环次数,时间间隔处决于处理器的运行速度,一般不建议使用            Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"));

12.CountdownEvent--与Sempaphore功能类似,但CountdownEvent支持动态调整信号计数

 示例:

        static void TimeLimitShopping(int custCount,int times,CountdownEvent countdown)        {            var customers = Enumerable.Range(1, custCount);            foreach (var customer in customers)            {                int currentCustomer = customer;               Task.Factory.StartNew(()=>                {                    SpinWait.SpinUntil(() => false, 1000);                    Console.WriteLine("第{0}波客户购买情况:Customer-{1}-已购买.", times, currentCustomer);                    countdown.Signal();                });                //countdown.AddCount();            }        }//调用:var countdown = new CountdownEvent(5);                TimeLimitShopping(5, 1, countdown);                countdown.Wait();                countdown.Reset(10);                TimeLimitShopping(10, 2, countdown);                countdown.Wait();                countdown.Reset(20);                TimeLimitShopping(20, 3, countdown);                countdown.Wait();

 最后分享在System.Collections.Concurrent命名空间下的几个并发集合类:

ConcurrentBag<T>:表示线程安全的无序集合;

ConcurrentDictionary<T>:表示线程安全的多个键值对集合;

ConcurrentQueue<T>:表示线程安全的先进先出集合;

ConcurrentStack<T>:表示线程安全的后进先出集合;

线程的几个状态(以下图片来源于这篇文章:http://www.cnblogs.com/edisonchou/p/4848131.html):

参考以下相关文章:

归纳一下:C#线程同步的几种方法

C#编程总结(三)线程同步

 

  相关解决方案