本文主要描述在C#中线程同步的方法。线程的基本概念网上资料也不少就再也不赘述了。直接接入主题,在多线程开发的应用中,线程同步是不可避免的。在.Net框架中,实现线程同步主要经过如下的几种方式来实现,在MSDN的线程指南中已经讲了几种,本文结合做者实际中用到的方式一块儿说明一下。 多线程
1. 维护自由锁(InterLocked)实现同步
2. 监视器(Monitor)和互斥锁(lock)
3. 读写锁(ReadWriteLock)
4. 系统内核对象
1) 互斥(Mutex), 信号量(Semaphore), 事件(AutoResetEvent/ManualResetEvent)
2) 线程池
除了以上的这些对象以外实现线程同步的还可使用Thread.Join方法。这种方法比较简单,当你在第一个线程运行时想等待第二个线程执行结果,那么你可让第二个线程Join进来就能够了。
自由锁(InterLocked)
对一个32位的整型数进行递增和递减操做来实现锁,有人会问为何不用++或--来操做。由于在多线程中对锁进行操做必须是原子的,而++和--不具有这个能力。InterLocked类还提供了两个另外的函数Exchange, CompareExchange用于实现交换和比较交换。Exchange操做会将新值设置到变量中并返回变量的原来值: int oVal = InterLocked.Exchange(ref val, 1)。
监视器(Monitor)
在MSDN中对Monitor的描述是: Monitor 类经过向单个线程授予对象锁来控制对对象的访问。
Monitor类是一个静态类所以你不能经过实例化来获得类的对象。Monitor的成员能够查看MSDN,基本上Monitor的效果和lock是同样的,经过加锁操做Enter设置临界区,完成操做后使用Exit操做来释放对象锁。不过相对来讲Monitor的功能更强,Moniter能够进行测试锁的状态,所以你能够控制对临界区的访问选择,等待or离开, 并且Monitor还能够在释放锁以前通知指定的对象,更重要的是使用Monitor能够跨越方法来操做。Monitor提供的方法不多就只有获取锁的方法Enter, TryEnter;释放锁的方法Wait, Exit;还有消息通知方法Pulse, PulseAll。经典的Monitor操做是这样的:
框架
// 通监视器来建立临界区
static public void DelUser(string name)
{
try
{
// 等待线程进入
Monitor.Enter(Names);
Names.Remove(name);
Console.WriteLine("Del: {0}", Names.Count);
Monitor.Pulse(Names);
}
finally
{
// 释放对象锁
Monitor.Exit(Names);
}
}
}ide
其中Names是一个List<string>, 这里有一个小技巧,若是你想声明整个方法为线程同步可使用方法属性:函数
// 经过属性设置整个方法为临界区
[MethodImpl(MethodImplOptions.Synchronized)]
static public void AddUser(string name)
{
Names.Add(name);
Console.WriteLine("Add: {0}",Names.Count);
}测试
对于Monitor的使用有一个方法是比较诡异的,那就是Wait方法。在MSDN中对Wait的描述是: 释放对象上的锁以便容许其余线程锁定和访问该对象。
这里提到的是先释放锁,那么显然咱们须要先获得锁,不然调用Wait会出现异常,因此咱们必须在Wait前面调用Enter方法或其余获取锁的方法,如lock,这点很重要。对应Enter方法,Monitor给出来另外一种实现TryEnter。这两种方法的主要区别在因而否阻塞当前线程,Enter方法在获取不到锁时,会阻塞当前线程直到获得锁。不过缺点是若是永远得不到锁那么程序就会进入死锁状态。咱们能够采用Wait来解决,在调用Wait时加入超时时限就能够。spa
if (Monitor.TryEnter(Names))
{
Monitor.Wait(Names, 1000); // !!
Names.Remove(name);
Console.WriteLine("Del: {0}", Names.Count);
Monitor.Pulse(Names);
} 线程