多线程对共同资源的访问,往往会造成数据的混乱和不可预料的结果,因此一般需要加锁访问进行互斥访问。
所谓“互斥访问”是指,一段代码或者公共变量,在一个时刻只允许一个线程去访问,其他的线程需要等待,直到改线程处理完毕并通知下一个等待的线程去处理。
1、可以用“Lock”来提供代码的互斥访问:
- lock (一个类的示例、静态变量的类名)
- {
- // 代码
- }
2、可以用“Monitor”来提供变量的互斥访问:
- int money;
- Monitor.Enter(money);
- // money++或者money--;
- Monitor.Exit(money);
Monitor的工作原理是:Monitor维护了两个队列,一个是预备队列,其中保存准备获取锁的线程;另一个是等待队列,保存正在等待这个对象状态改变的线程。当锁被一个线程释放时,调用Monitor.Pulse()方法,将等待队列中的第一个线程放到预备队列中。
下面是一个使用Lock和Monitor的例子:
- using System;
- using System.Collections.Generic;
- using System.Text;
- using System.Threading;
- namespace Demo
- {
- public class ProductClass
- {
- int products;
- bool enableGet = false; // If ture, consumer can get product, otherwise producer is producing.
- public int GetProducts()
- {
- lock (this)
- {
- if (!enableGet)
- {
- try
- {
- Monitor.Wait(this);
- }
- catch (SynchronizationLockException ex)
- {
- Console.WriteLine("[SynchronizationLockException Error]: " + ex.ToString());
- }
- catch (ThreadInterruptedException ex)
- {
- Console.WriteLine("[ThreadInterruptedException Error]: " + ex.ToString());
- }
- }
- Console.WriteLine("Consume: {0}", products);
- // Reset to be false after getting products.
- enableGet = false;
- // Notify producing thread to produce products
- Monitor.Pulse(this);
- }
- return products;
- }
- public void ProduceProducts(int products)
- {
- lock (this)
- {
- if (enableGet)
- {
- try
- {
- Monitor.Wait(this);
- }
- catch (SynchronizationLockException ex)
- {
- Console.WriteLine("[SynchronizationLockException Error]: " + ex.ToString());
- }
- catch (ThreadInterruptedException ex)
- {
- Console.WriteLine("[ThreadInterruptedException Error]: " + ex.ToString());
- }
- }
- this.products = products;
- Console.WriteLine("Produce: {0}", products);
- enableGet = true;
- Monitor.Pulse(this);
- }
- }
- }
- public class Producer
- {
- ProductClass aProductClass;
- int produceTimes = 1;
- public Producer(ProductClass aProductClass, int produceTimes)
- {
- this.aProductClass = aProductClass;
- this.produceTimes = produceTimes;
- }
- public void ThreadRun()
- {
- for (int i = 0; i < produceTimes; i++)
- {
- aProductClass.ProduceProducts(i);
- }
- }
- }
- public class Consumer
- {
- ProductClass aProductClass;
- int getProductTimes = 1;
- public Consumer(ProductClass aProductClass, int getProductTimes)
- {
- this.aProductClass = aProductClass;
- this.getProductTimes = getProductTimes;
- }
- public void ThreadRun()
- {
- int returnValue;
- for (int i = 0; i < getProductTimes; i++)
- {
- returnValue = aProductClass.GetProducts();
- }
- }
- }
- class Program
- {
- static void Main(string[] args)
- {
- ProductClass aProductClass = new ProductClass();
- Producer aProducer = new Producer(aProductClass, 20);
- Consumer aConsumer = new Consumer(aProductClass, 20);
- Thread produceThread = new Thread(new ThreadStart(aProducer.ThreadRun));
- Thread consumeThread = new Thread(new ThreadStart(aConsumer.ThreadRun));
- try
- {
- produceThread.Start();
- consumeThread.Start();
- produceThread.Join();
- consumeThread.Join();
- Console.ReadLine();
- }
- catch (ThreadStateException ex)
- {
- Console.WriteLine("[ThreadStateException Error]: " + ex.ToString());
- }
- catch (ThreadInterruptedException ex)
- {
- Console.WriteLine("[ThreadInterruptedException Error]: " + ex.ToString());
- }
- }
- }
- }