C#中使用lock和Monitor控制多线程对资源的使用,最多见的生产者和消费者问题就是多线程同步和通讯的经典例子。了解C#多线程的同步与通讯。express
1、关于lock和Monitor多线程
lock能够把一段代码定义为互斥段(critical section),互斥段在一个时刻内只容许一个线程进入执行,而其它线程必须等待。格式定义以下:函数
lock(expression) statement_block
expression表明要跟踪的对象,一般是引用。通常地,若是想保护一个类的实例,使用this;若是保护一个静态变量(如互斥代码段在一个静态方法内部),使用类名就能够了。而statement_block就是互斥段的代码。this
Monitor用于多线程公用一个对象时使线程共享资源的方案。Monitor必须和一个具体的对象相关联。spa
2、生产者和消费者问题线程
假设两个线程同时维护一个队列,若是一个线程对队列中更新元素,而另一个线程从队列中获取元素,那么咱们称更新元素的线程为生产者,称获取元素的线程为消费者。code
一、被操做对象对象
/// <summary>; /// 被操做对象 /// </summary>; public class Counter { //更新和读取的数字 private int numberOfCounter; //读操做可执行标记,能够防止死锁的发生 private bool readFlag = false; public void Read() { //锁定后,其它读操做等待这一次读操做完成 lock (this) { //第一次之行为flase,进入等待 if (!readFlag) { try { //进入等待读,另外一个线程写 Monitor.Wait(this); } catch (Exception ex) { Console.WriteLine(ex); } } Console.WriteLine("消费(获取): {0}", numberOfCounter); //重置,消费已经完成 readFlag = false; Monitor.Pulse(this); } } public void Write(int number) { //锁定后,其它写操做等待这一次写操做完成 lock (this) { //第一次readFlag为flase,跳过执行下边的写 //若是当前正在读,等待读操做执行Monitor.Pulse if (readFlag) { try { Monitor.Wait(this); } catch (Exception ex) { Console.WriteLine(ex); } } numberOfCounter = number; Console.WriteLine("生产(更新): {0}", numberOfCounter); //重置,生产已经完成 readFlag = true; //同步经过等待Pulse来完成 Monitor.Pulse(this); } } }
二、生产者和消费者blog
/// <summary>; /// 生产者 /// </summary> public class CounterWrite { Counter counter; //生产者生产次数 int quantity = 1; public CounterWrite(Counter box, int request) { //构造函数 counter = box; quantity = request; } //生产者向操做对象更新信息 public void Write() { for (int i = 1; i <= quantity; i++) counter.Write(i); } } /// <summary> /// 消费者 /// </summary> public class CounterRead { Counter counter; //生产者生产次数 int quantity = 1; public CounterRead(Counter box, int request) { //构造函数 counter = box; quantity = request; } //消费者从操做对象中获取信息 public void Read() { for (int i = 1; i <= quantity; i++) counter.Read(); } }
三、线程操做队列
Counter counter = new Counter(); CounterRead read = new CounterRead(counter, 10); CounterWrite write = new CounterWrite(counter, 10); Thread th1 = new Thread(new ThreadStart(read.Read)); Thread th2 = new Thread(new ThreadStart(write.Write)); th1.Start(); th2.Start(); th1.Join(); th2.Join(); Console.ReadLine();
经过lock锁定Counter对象的引用,初始readFlag为false控制线程1等待读取:Monitor.Wait(this),线程2写入,而后更改readFlag,而后执行:Monitor.Pulse(this),通知等待队列中的线程请求对象状态已发生改变,线程1锁定this,执行读操做,而后更改readFlag,线程1和线程2交互执行写读的操做。同时由于readFlag的存在和交替更新,避免了死锁状况的发生。