1.锁的种类和用途
a. 简单锁 b.互锁 c.读写锁
多个线程同时访问同一项资源,可能导致死锁和饥饿等问题。
2. 简单锁(互斥锁)
锁最简单的方式是使用lock语句,最适合保护不超过一个方法的小型代码块。
不同锁定公有类型(lock(typeof(PublicType))或类型实例(lock(this))
private int counter;
private static readonly object synclock = new object();
public int Increment()
{
lock(synclock)
{
return this.counter++
}
}
public int Decrement()
{
lock(synclock)
{
return this.counter++;
}
}
3. Interlocked 原子锁 轻量级
interlocked系列方法提供了对简单类型的原子操作(不会被打断的操作)。
什么说它是轻量级呢?因为它仅对整形数据(即int类型,long也行)进行同步。如果你学过操作系统里面的PV操作(即信号量),那么你对它已经了解了一般。它实现的正是如同信号量的功能。
Interlocked.Increment(ref value)
数值加一(原子性操作)
Interlocked.Decrement(ref value)
数值减一(原子性操作)
Interlocked.Exchange(ref value1, value2)
交换:把值2赋给值1;返回新值
Interlocked.CompareExchange(ref value1, value2, value3)
实现比较和交换两种功能:值1和值3比较,如果相同,把值2给值1,不相同则不作任何操作;返回原值(多用于判断条件)
3.1 以原子方式递增
int i = 0 ;
Interlocked.Increment( ref i);
3.2 单例模式中的双检测技术
internal sealed class MySingleton
{
private static MySingleton s_value = null;
public static MySingleton GetMySingleton()
{
if(s_value != null) return s_value;
MySingleton temp = new MySingleton();
Interlocked.CompareExchange(ref s_value, temp. null);
return s_value;
}
}
- ReadWriterLock 读写锁
加粗样式(1)有时对于共享资源应当区分读和写,因为读的时候往往是允许多线程同时读的,因为这不会造成混乱;而只有在需要写的时候才不允许其他线程读或者写。
(2).NET的ReaderWriterLock和ReaderWriterLockSlim为我们提供了区分读和写的锁。这种方式在有些情况下通常比Monitor更高效。
(3)在MSDN中推荐使用的是ReaderWriterLockSlim类,其解释是ReaderWriterLockSlim用一种简单的规则处理递归调用以及更好的支持锁升级机制,而且能更好的避免死锁的发生,最后它比ReaderWriterLock更高效。
4.1 读写锁的三种模式
Read Mode 读模式:表示线程试图对共享资源进行读操作,而不会写。ReaderWriterLockSlim.EnterReadLock
ReaderWriterLockSlim.TryEnterReadLock
WriteMode 写模式:表示线程对共享资源进行写操作。
ReaderWriterLockSlim.EnterWriteLock
ReaderWriterLockSlim.TryEnterWriteLock
Upgradeable Read Mode:读模式,但可能将来升级成写锁。ReaderWriterLockSlim.EnterUpgradeableReadLock
ReaderWriterLockSlim.TryEnterUpgradeableReadLock
读锁升级
同一时刻只能有一个线程获得可升级读锁,当获得可升级读锁的线程试图获得写锁的时候或可以调用EnterWriteLock,如果此时有线程没有释放写锁的话,EnterWriteLock会阻塞直到所有的读锁释放,同时试图获得读锁的线程也将阻塞(这里不用考虑写锁,因为既然可以获得可升级读锁,那么必然不存在写锁),这有点像“关门放狗”,关上门不让狗进来,而把已经在里面的狗放走)
4.2 例子
读/写模式
private ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim();
private Dictionary<int, string> innerCache = new Dictionary<int, string>();
public int Count
{ get { return innerCache.Count; } }
public string Read(int key)
{
cacheLock.EnterReadLock();
try
{
return innerCache[key];
}
finally
{
cacheLock.ExitReadLock();
}
}
public void Delete(int key)
{
cacheLock.EnterWriteLock();
try
{
innerCache.Remove(key);
}
finally
{
cacheLock.ExitWriteLock();
}
}
5.使用通知
使用手动重置通知ManualResetEvent/ManualResetEventSlim 类实现
通知一个或多个正在等待的线程已发生事件
5.1 例子
private ManualResetEvent _mre = new ManualResetEvent(true);
public void AddMsg(BusMsg msg)
{
if(msg == null) return;
lock(_lockObj)
{
_taskQueue.Enqueue(msg);
_mre.Set(); //释放线程
}
}
public void Run()
{
BusMsg msg = null;
while(true)
{
_mre.WaiteOne();//等待,阻止当前线程,直到当前 WaitHandle 收到信号。
if(_taskQueue.Count == 0)
{
_mre.Reset(); //重启线程
continnue;
}
msg = GetMsg();
if(msg!=null)
DoBusMsg(msg);
else
_mre.Reset();