C#线程同步

原子操作类:

 public abstract class CounterBase
    {
        public abstract void Increase();
        public abstract void Decrease();
    }

  

 public class CounterNoInterlocked : CounterBase
    {
        public int Count { get; private set; }
        public override void Decrease()
        {
            Count--;
        }

        public override void Increase()
        {
            Count++;
        }
    }

  

 public class CounterWithInterlocked : CounterBase
    {
        private int _count;
        public int Count { get { return _count; } }

        public override void Decrease()
        {
            Interlocked.Decrement(ref _count);
        }

        public override void Increase()
        {
            Interlocked.Increment(ref _count);
        }
    }

  Mutex类:

 static void Main(string[] args)
        {            
            // 应用场景:WinForm应用程序只能启动一个
            string mutexName = "互斥量";
            using (var mutex = new Mutex(false, mutexName))
            {
                if (mutex.WaitOne(TimeSpan.FromSeconds(5), false))
                {
                    Console.WriteLine("运行中....");
                    Console.ReadLine();
                    mutex.ReleaseMutex();
                }
                else
                {
                    Console.WriteLine("第二个程序运行啦。。。。");
                }
            }
            Console.WriteLine("Hello World!");
        }

  SemaphoreSlim:

static SemaphoreSlim _semaphoreSlim = new SemaphoreSlim(10);

        /// <summary>
        /// 在餐厅吃饭
        /// </summary>
        /// <param name="seconds"></param>
        static void EatInDiningRoom(int seconds)
        {
            Console.WriteLine($"{Thread.CurrentThread.Name}等待座位...");
            _semaphoreSlim.Wait();
            Console.WriteLine($"{Thread.CurrentThread.Name}坐下来开始吃饭...");
            Thread.Sleep(TimeSpan.FromSeconds(seconds));
            Console.WriteLine($"{Thread.CurrentThread.Name}吃完了!");
            _semaphoreSlim.Release();
        }

        static void Main(string[] args)
        {
            // SemaphoreSlim:信号量

            // 假设餐厅只有10个位置,但有15位顾客要吃饭,所以有5位顾客需要等待
            for (int i = 0; i < 15; i++)
            {
                Thread t = new Thread(() => {
                    EatInDiningRoom(new Random().Next(2, 4));
                });
                t.Name = $"t{i}";
                t.Start();
            }

            Console.ReadKey();
        }

  AutoResetEvent:

 //false:无信号,子线程的WaitOne方法不会被自动调用;
        //true:有信号,子线程的WaitOne方法会被自动调用。
        // 只有一个线程可以看到信号的改变
        static AutoResetEvent _resetEvent = new AutoResetEvent(false);
        static int number = -1;

        public static void Start()
        {
            Thread t = new Thread(ReadThreadProc);
            t.Name = "Read";
            t.Start();

            for (int i = 0; i < 100; i++)
            {
                Console.WriteLine($"Wirte线程写入数字:{i}");
                number = i;
                
                //发信号,通知正在等待的读线程写入操作已完成
                _resetEvent.Set();

                // 暂停1毫秒,给读线程时间读取数字
                Thread.Sleep(1);
            }
            //t.Abort();
        }

        static void ReadThreadProc()
        {
            while (true)
            {
                //读线程等待写线程写入数字
                _resetEvent.WaitOne();
                Console.WriteLine($"{Thread.CurrentThread.Name}线程读取到数字:{number}");
            }
        }

  

 class Sample2
    {
        public static void Start()
        {
            RemoteRequest request = new RemoteRequest();

            new Thread(request.RequestInterfaceA).Start();
            new Thread(request.RequestInterfaceB).Start();

            AutoResetEvent.WaitAll(request._resetEvents.ToArray());

            request.RequestInterfaceC();

        }
    }

    class RemoteRequest
    {
        public IList<AutoResetEvent> _resetEvents;

        public RemoteRequest()
        {
            _resetEvents = new List<AutoResetEvent>();
            _resetEvents.Add(new AutoResetEvent(false));
            _resetEvents.Add(new AutoResetEvent(false));
        }

        public void RequestInterfaceA()
        {
            Console.WriteLine("异步调用远程接口A获取用户数据...");
            Thread.Sleep(TimeSpan.FromSeconds(2));
            _resetEvents[0].Set();
            Console.WriteLine("接口A数据获取完成!");
        }

        public void RequestInterfaceB()
        {
            Console.WriteLine("异步调用远程接口B获取订单数据...");
            Thread.Sleep(TimeSpan.FromSeconds(1));
            _resetEvents[1].Set();
            Console.WriteLine("接口B订单数据获取完成!");
        }

        public void RequestInterfaceC()
        {
            Console.WriteLine("接口A和接口B的数据获取完成,开始处理C接口数据...");
        }
    }

  ManualResetEventSlim:

 static ManualResetEventSlim _event = new ManualResetEventSlim(false);

        static void Main(string[] args)
        {
            var t1 = new Thread(() => { ThroughGates(5); });
            var t2 = new Thread(() => { ThroughGates(6); });
            var t3 = new Thread(() => { ThroughGates(12); });
            t1.Name = "t1";
            t2.Name = "t2";
            t3.Name = "t3";
            t1.Start();
            t2.Start();
            t3.Start();

            Console.WriteLine("主线程暂停6秒");
            Thread.Sleep(TimeSpan.FromSeconds(6));
            Console.WriteLine("主线程打开传送门...");
            _event.Set();

            Console.WriteLine("主线程暂停2秒");
            Thread.Sleep(TimeSpan.FromSeconds(2));
            Console.WriteLine("主线程关闭传送门...");
            _event.Reset();

            Console.WriteLine("主线程暂停10秒");
            Thread.Sleep(TimeSpan.FromSeconds(10));
            Console.WriteLine("主线程第二次打开传送门...");
            _event.Set();

            Console.WriteLine("主线程暂停2秒");
            Thread.Sleep(TimeSpan.FromSeconds(2));
            Console.WriteLine("主线程第二次关闭传送门...");
            _event.Reset();

            Console.ReadKey();
        }

        static void ThroughGates(int seconds)
        {
            Console.WriteLine($"线程{Thread.CurrentThread.Name}进入传送门之前先暂停{seconds}秒。");
            //for (int i = 1; i <= seconds; i++)
            //{
            //    Console.Write($"{i}...");
            //    Thread.Sleep(TimeSpan.FromSeconds(1));
            //}
            Thread.Sleep(TimeSpan.FromSeconds(seconds));
            Console.WriteLine($"线程{Thread.CurrentThread.Name}暂停完毕,等待传送门的开启。");
            _event.Wait();
            Console.WriteLine($"线程{Thread.CurrentThread.Name}进入传送门!");
        }

  CountdownEvent:

 static CountdownEvent _event = new CountdownEvent(3);

        static void Test(int seconds)
        {
            Console.WriteLine($"{Thread.CurrentThread.Name}线程开始执行...");
            Thread.Sleep(TimeSpan.FromSeconds(seconds));
            Console.WriteLine($"{Thread.CurrentThread.Name}线程执行完成!") ;
            _event.Signal();
        }

        static void Main(string[] args)
        {
            Thread t1 = new Thread(() => { Test(2); });
            t1.Name = "t1";
            Thread t2 = new Thread(() => { Test(4); });
            t2.Name = "t2";
            Thread t3 = new Thread(() => { Test(6); });
            t3.Name = "t3";

            t1.Start();
            t2.Start();
            t3.Start();

            _event.Wait();
            Console.WriteLine("所有的线程执行完毕!");
            _event.Dispose();

            Console.ReadKey();
        }

  Barrier:

 // 当您需要一组任务并行地运行一连串的阶段,
        // 但是每一个阶段都要等待所有其他任务都完成前一阶段之后才能开始,
        // 你可以通过Barrier实例来同步这一类协同工作
        static Barrier _barrier = new Barrier(3, b => {
            Console.WriteLine($"=========当前是{b.CurrentPhaseNumber+1}阶段===============");
        });

        static void Test(string step1, string step2, string step3, int seconds)
        {
            string threadName = Thread.CurrentThread.Name;
            Console.WriteLine($"线程{threadName}开始执行第一阶段操作:{step1}");
            Thread.Sleep(TimeSpan.FromSeconds(seconds));
            _barrier.SignalAndWait();

            Console.WriteLine($"线程{threadName}开始执行第二阶段操作:{step2}");
            Thread.Sleep(TimeSpan.FromSeconds(seconds));
            _barrier.SignalAndWait();

            Console.WriteLine($"线程{threadName}开始执行第三阶段操作:{step3}");
            Thread.Sleep(TimeSpan.FromSeconds(seconds));
            _barrier.SignalAndWait();

            Console.WriteLine($"线程{threadName}所有操作执行完毕");
        }

        static void Main(string[] args)
        {
            Thread t1 = new Thread(() => { Test("步兵列阵", "步兵冲锋", "步兵退守", 3); });
            t1.Name = "t1";
            Thread t2 = new Thread(() => { Test("骑兵列阵", "骑兵冲锋", "骑兵退守", 4); });
            t2.Name = "t2";
            Thread t3 = new Thread(() => { Test("炮兵列阵", "炮兵装弹", "炮兵开炮", 5); });
            t3.Name = "t3";

            t1.Start();
            t2.Start();
            t3.Start();

            Console.ReadKey();
        }

  ReaderWriterLockSlim

    class Program
    {
        static void Main(string[] args)
        {
            new Thread(Read) { IsBackground = true }.Start();
            new Thread(Read) { IsBackground = true }.Start();
            new Thread(Read) { IsBackground = true }.Start();

            new Thread(() => Write("线程 1"))
            {
                IsBackground = true
            }.Start();
            new Thread(() => Write("线程 2"))
            {
                IsBackground = true
            }.Start();

            Thread.Sleep(TimeSpan.FromSeconds(30));

            Console.ReadKey();
        }

        //读写锁的概念很简单,允许多个线程同时获取读锁,但同一时间只允许一个线程获得写锁,因此也称作共享-独占锁。
        //在C#中,推荐使用ReaderWriterLockSlim类来完成读写锁的功能。
        //某些场合下,对一个对象的读取次数远远大于修改次数,如果只是简单的用lock方式加锁,则会影响读取的效率。
        //而如果采用读写锁,则多个线程可以同时读取该对象,只有等到对象被写入锁占用的时候,才会阻塞。
        private static ReaderWriterLockSlim _rw = new ReaderWriterLockSlim();
        private static Dictionary<int, int> _item = new Dictionary<int, int>();

        private static void Read()
        {
            Console.WriteLine("①读取字典的内容");
            while (true)
            {
                try
                {
                    //尝试进入读取模式锁定状态。
                    _rw.EnterReadLock();
                    foreach (var key in _item.Keys)
                    {
                        Console.WriteLine("③读取模式---键 {0} ", key);
                        Thread.Sleep(TimeSpan.FromSeconds(0.1));
                    }
                }
                finally
                {
                    //减少读取模式的递归计数,并在生成的计数为 0(零)时退出读取模式。
                    _rw.ExitReadLock();
                }
            }
        }

        private static void Write(string threadName)
        {
            while (true)
            {
                try
                {
                    int newKey = new Random().Next(250);
                    //可升级读模式,这种方式和读模式的区别在于它还有通过调用 EnterWriteLock 或 TryEnterWriteLock 方法升级为写入模式
                    //因为每次只能有一个线程处于可升级模式。进入可升级模式的线程,不会影响读取模式的线程,即当一个线程进入可升级模式,
                    //任意数量线程可以同时进入读取模式,不会阻塞。
                    //如果有多个线程已经在等待获取写入锁,那么运行EnterUpgradeableReadLock将会阻塞,直到那些线程超时或者退出写入锁。
                    _rw.EnterUpgradeableReadLock();
                    if (!_item.ContainsKey(newKey))
                    {
                        try
                        {
                            //尝试进入写入模式锁定状态
                            _rw.EnterWriteLock();
                            _item[newKey] = 1;
                            Console.WriteLine("②新键 {0} 添加到字典中通过 {1}", newKey, threadName);
                        }
                        finally
                        {
                            //减少写入模式的递归计数,并在生成的计数为 0(零)时退出写入模式。
                            _rw.ExitWriteLock();
                        }
                    }
                    Thread.Sleep(TimeSpan.FromSeconds(0.1));
                }
                finally
                {
                    _rw.ExitUpgradeableReadLock();
                }
            }
        }
    }

  

posted @ 2020-05-30 14:41  石shi  阅读(305)  评论(0编辑  收藏  举报