锁定与同步
CLR怎样实现lock(obj)锁定?
从原理上讲,lock和Syncronized Attribute都是用Moniter.Enter实现的,比如如下代码
lock(obj){
//do things
}
在编译时,会被编译为类似
Moniter.Enter(obj){
//do things
}
}
catch{}
finally{
Moniter.Exit(obj);
}
而[MethodImpl(MethodImplOptions.Synchronized)]标记为同步的方法会在编译时被lock(this)语句所环绕
所以我们只简单探讨Moniter.Enter的实现
(注:DotNet并非使用Win32API的CriticalSection来实现Moniter.Enter,不过他为托管对象提供了一个类似的结构叫做Syncblk)
每个对象实例头部都有一个指针,这个指针指向的结构,包含了对象的锁定信息,当第一次使用Moniter.Enter(obj)时,这个obj对象的锁定结构就会被初时化,第二次调用Moniter.Enter时,会检验这个object的锁定结构,如果锁没有被释放,则调用会阻塞
WaitHandle是什么,他和他的派生类怎么使用
WaitHandle是Mutex,Semaphore,EventWaitHandler,AutoResetEvent,ManualResetEvent共同的祖先,他们包装了用于同步的内核对象,也就是说是这些内核对象的托管版本。
Mutex:类似于一个接力棒,拿到接力棒的线程才可以开始跑,当然接力棒一次只属于一个线程(Thread Affinity),如果这个线程不释放接力棒(Mutex.ReleaseMutex),那么没办法,其他所有需要接力棒运行的线程都知道能等着看热闹
Semaphore:类似于一个小桶,里面装了几个小球,凡是拿到小球就可以跑,比如指定小桶里最初有四个小球,那么开始的四个线程就可以直接拿着自己的小球开跑,但是第五个线程一看,小球被拿光了,就只好乖乖的等着有谁放一个小球到小桶里(Semophore.Release),他才能跑,但是这里的游戏规则比较特殊,我们可以随意向小桶里放入小球,也就是说我可以拿走一个小球,放回去俩,甚至一个都不拿,放回去5个,这样就有五个线程可以拿着这些小球运行了.我们可以规定小桶里有开始有几个小球(构造函数的第一个参数),也可以规定最多不能超过多少小球(构造函数的第二个参数)
ManualResetEvent,AutoResetEvent可以参考http://www.cnblogs.com/uubox/archive/2007/12/18/1003953.html
什么是用双锁实现Singleton,为什么要这样做,双锁检验是不安全的吗?
使用双锁检验技巧来实现单件,来自于Java社区
get{
if(_instance!=null)}{
lock(_instance){
if(s_value==null){
_instance= new MySingleton();
}
}
}
}
}
这样做其实是为了提高效率,比起
get{
lock(_instance){
if(s_value==null){
_instance= new MySingleton();
}
}
前一种方法在instance创建的时候不需要用lock同步,从而增进了效率
在java中这种技巧被证明是不安全的详细见http://www.cs.umd.edu/~pugh/java/memoryModel/
但是在.Net下,这样的技巧是成立的,因为.Net使用了改进的内存模型
并且在.Net下,我们可以使用LazyInit来实现单件
public static MySingleton Instance{
get{return _instance}
}
当第一此使用_instance时,CLR会生成这个对象,以后再访问这个字段,将会直接返回
互斥对象(Mutex),信号量(Semaphore),事件(Event)对象与lock语句的比较
首先这里所谓的事件对象不是System.Event,而是一种用于同步的内核机制
互斥对象和事件对象属于内核对象,利用内核对象进行线程同步,线程必须要在用户模式和内核模式间切换,所以一般效率很低,但利用互斥对象和事件对象这样的内核对象,可以在多个进程中的各个线程间进行同步。
lock或者Moniter是.net用一个特殊结构实现的,不涉及模式切换,也就是说工作在用户方式下,同步速度较快,但是不能跨进程同步
