关于多线程学习总结(四) 锁

来源:互联网 发布:spine3.4 破解版 mac 编辑:程序博客网 时间:2024/06/07 05:32

写在前面

在进入锁的学习前来看看Thread的方法,之前一直对这个方法不了解,今天学习了下。在学习之前看两段代码吧:

复制代码
 1 static void Main(string[] args) 2 { 3             Thread thread = new Thread(new ThreadStart(myThread1)); 4             thread.Start(); 5             thread.Join();  //关键这一行 6             Console.WriteLine("主线程"); 7             Console.ReadKey(); 8  } 9 public static void myThread1()10 {11             Thread.Sleep(1000);12             Console.WriteLine("1测试线程{0}",++count1);13 }
复制代码

先来看看效果再说话(左边截图为5行未被注释,右边为被注释):

在这之前,小弟一直不明白为什么加了上面第5行与不加第五行区别是什么,今天终于知道了,原来是线程之间原本并行执行通过使用Join()使其串行化,在这个例子里myThread1()被调用,而此方法存在一个线程阻塞,此时先打印“主线程”(上 右图);然而调用了Join()方法,使其原本并行化的线程串行化,所以主线程必须等待子线程执行完才能执行,此时先打印“1测试线程1”(上 左图)。<以上的串行与并行用词有点不严谨,主要事为了方便理解而已>

进入锁的学习

此句网上COPY来的:我们抛开.NET环境看线程同步,无非是执行两种操作:一是互斥/加锁,目的是保证临界区代码操作的“原子性”;另一种是信号灯操作,目的是保证多个线程按照一定顺序执行,如生产者线程要先于消费者线程执行。

暂时先脑海中留点锁的印象就好啦!下面介绍两个类:

Monitor类

通过查MSDN我们可以发现Monitor类一共有17个方法,但是这17个方法并不是都常用,下面我简单列举几个介绍并结合实例理解理解:

一、Enter()与Exit()

  在我看来它目前最主要的功能是设置边界,使原本并行执行的线程顺序化,此句小弟断章取义,不对请指正

复制代码
 1         static void Main(string[] args) 2         { 3             for (int i = 0; i < 15; i++) 4             { 5                 Thread thread = new Thread(new ThreadStart(myThread1)); 6                 thread.Start(); 7             } 8             Console.WriteLine("主线程"); 9             Console.ReadKey();10         }11         static object ob = new object();12         static int count1 = 0;13         public static void myThread1()14         {15             Monitor.Enter(ob);  //作用域开始16             Thread.Sleep(10);17             Console.WriteLine("1测试线程{0}", ++count1);18             Monitor.Exit(ob);  //作用域结束19         }
复制代码

看看效果再说话(左边截图为使用Enter()和Exit(),右边木有用):

看到区别木有,话说程序员都是聪明的班子,哈哈哈、、、、

二、Wait()与Pulse()

我们先来看看MSDN的官方介绍

Wait()——释放对象上的锁并阻止当前线程,直到它重新获取该锁

Pulse()——通知等待队列中的线程锁定对象状态的更改

简而言之,Wait()方法就是暂时释放资源锁,线程进入等待队列,此时其它线程获取资源锁;Pulse()方法则是唤醒等待队列中的线程,重新得到资源锁。

复制代码
 1         static void Main(string[] args) 2         { 3             for (int i = 0; i < 5; i++) 4             { 5                 Thread thread = new Thread(new ThreadStart(myThread1)); 6                 thread.Start(); 7                 Thread thread1 = new Thread(new ThreadStart(myThread2)); 8                 thread1.Start(); 9             }10             Console.WriteLine("主线程");11         }12         static object ob = new object();  13         static int count1 = 0;14         static int count2 = 0;15         public static void myThread1()16         {17             Monitor.Enter(ob);18             Thread.Sleep(10);19             Console.WriteLine("1测试线程{0}", ++count1);20             Monitor.Wait(ob);21             Console.WriteLine("wait");22             Monitor.Pulse(ob);23             Monitor.Exit(ob);24         }25         public static void myThread2()26         {27             Monitor.Enter(ob);28             Thread.Sleep(10);29             Console.WriteLine("2测试线程{0}", ++count2);30             Monitor.Wait(ob);31             Console.WriteLine("wait2");32             Monitor.Pulse(ob);33             Monitor.Exit(ob);34         }
复制代码

运行结果如下图:

上面打印交替次数是没有规律的,每次都会有偏差

再总结几点:

1.Monitor.Pulse()调用后线程还是会执行下一行代码,不会执行另一个线程,除非再调用Monitor.Wait()让线程进入等待状态

2.只有锁的当前所有者可以使用 Pulse 向等待对象发出信号

3.当前拥有指定对象上的锁的线程调用此方法以便向队列中的下一个线程发出锁的信号。接收到脉冲后,等待线程就被移动到就绪队列中。 在调用 Pulse 的线程释放锁后,就绪队列中的下一个线程(不一定是接收到脉冲的线程)将获得该锁(此句来自MSDN)

ReaderWriterLock

说句实话,这个类我还真不太熟悉,简单介绍我知道的吧!

我们都知道我们日常代码中,绝大部分都是读取,少部分为写入,而我们前面学习的Monitor类在这里就有些不适合了,因此就出现了ReaderWriterLock这个类,它的好处就是起到,并实现了”写入串行“,”读取并行“的神奇效果。