C#多线程同步重新梳理

线程同步这一方面,因为相对来说用得比较少,每次做多线程相关的问题是都必须重新温习,今天在看异步TCP时,又重新梳理了下。

多线程同步大概有两类:一类是信号量,另一类是互斥,排他

1.信号量

 

实例代码如:

 class Program

    {
        private int n1, n2, n3;
        EventWaitHandle myEventWaitHandle = new EventWaitHandle(false, EventResetMode.ManualReset);    //EventResetMode中有手动和自动设置信号量;

                //false没默认没有信号,true表示默认有信号。对应的类AutoResetEvent和ManualResetEvent

        static void Main(string[] args)
        {
            Program p = new Program();
            Thread t0 = new Thread(new ThreadStart(p.WriteThread));
            Thread t1 = new Thread(new ThreadStart(p.ReadThread));
           
          
            t1.Start();
            t0.Start();
            Thread t3 = new Thread(new ThreadStart(p.AA));
            t3.Start();
            Console.ReadLine();
        }
        private void WriteThread() 
        {
            //允许其他需要等待的线程阻塞
            myEventWaitHandle.Reset();    //获得信号并让其他的wait处理等待状态
            Console.WriteLine("t1");
            n1 = 1; 
            n2 = 2;
            n3 = 3;
            myEventWaitHandle.Set();   //允许其他等待的线程继续
        }
        private void ReadThread()
        {
           
            myEventWaitHandle.WaitOne();    //阻塞当前线程,直到收到信号
            Console.WriteLine("{0}+{1}+{2}={3}", n1, n2, n3, n1 + n2 + n3);
        }
        private void AA() 
        {
            myEventWaitHandle.WaitOne();   //阻塞当前线程,直到收到信号
            string aa = "";
        }
   

//AutoResetEvent和ManualResetEvent的区别是在waitone后,自动会变成无信号状态,而手动不会,如下面的实例如果设置成手动,则都可以执行,如果设置成

自动, ReadThread和AA方法有一个方法执行不到,因为waitone在自动获得信号后变成了信号状态。

    }

 2.lock关键字

      lock是C#关键词,它将语句块标记为临界区,确保当一个线程位于代码的临界区时,另一个线程不进入临界区。如果其他线程试图进入锁定的代码,则它将一直等待(即被阻止),直到该对象被释放。方法是获取给定对象的互斥锁,执行语句,然后释放该锁。

      MSDN上给出了使用lock时的注意事项通常,应避免锁定 public 类型,否则实例将超出代码的控制范围

3.Monitor类

 lock就是对Monitor的Enter和Exit的一个封装,而且使用起来更简洁,因此Monitor类的Enter()和Exit()方法的组合使用可以用lock关键字替代 

另外Monitor类还有几个常用的方法:

      TryEnter()能够有效的解决长期死等的问题,如果在一个并发经常发生,而且持续时间长的环境中使用TryEnter,可以有效防止死锁或者长时间 的等待。比如我们可以设置一个等待时间bool gotLock = Monitor.TryEnter(myobject,1000),让当前线程在等待1000秒后根据返回的bool值来决定是否继续下面的操作。

      Wait()释放对象上的锁以便允许其他线程锁定和访问该对象。在其他线程访问对象时,调用线程将等待。脉冲信号用于通知等待线程有关对象状态的更改。

      Pulse(),PulseAll()向一个或多个等待线程发送信号。该信号通知等待线程锁定对象的状态已更改,并且锁的所有者准备释放该锁。等待线程被 放置在对象的就绪队列中以便它可以最后接收对象锁。一旦线程拥有了锁,它就可以检查对象的新状态以查看是否达到所需状态。

      注意:Pulse、PulseAll和Wait方法必须从同步的代码块内调用。

  class Program
    {
        private static object objA = new object();
        private static object objB = new object();
        static void Main(string[] args)
        {
            Thread threadA = new Thread(LockA);
            Thread threadB = new Thread(LockB);
            threadA.Start();
            threadB.Start();
            Thread.Sleep(4000);
            Console.WriteLine("线程结束");
            
            Console.ReadLine();
        }
        public static void LockA()
        {
            if (Monitor.TryEnter(objA, 1000))
            {
                Thread.Sleep(1000);
                if (Monitor.TryEnter(objB, 2000))
                {
                    Monitor.Exit(objB);
                }
                else
                {
                    Console.WriteLine("LockB timeout");
                } 
                Monitor.Exit(objA);
            }
            Console.WriteLine("LockA");
        }
        public static void LockB()
        {
            if (Monitor.TryEnter(objB, 2000))
            {
                Thread.Sleep(2000);
                if (Monitor.TryEnter(objA, 1000))
                {
                    Monitor.Exit(objA);
                }
                else
                {
                    Console.WriteLine("LockA timeout");
                }
                Monitor.Exit(objB);
            }
            Console.WriteLine("LockB");
        }
    }

 本来未产生死锁的,但什么有个超时时间,所以不会发生死锁

4Mutex 

EventWaitHandle的名字与Mutex差了很多,不过它可是Mutex不折不扣的兄弟——它和Mutex都是WaitHandle的子类,用法也差不多 

原文地址:https://www.cnblogs.com/fujinliang/p/2552841.html