Spiga

【经验总结】C#常用线程同步方法应用场景和实现原理

2010-12-21 11:31 by GUO Xingwang, 1857 visits, 收藏, 编辑

  简单描述volatile,Interlocked,lock,Mutex,Semaphore,Spin lock,AutoResetEvent,ManualResetEvent,ReaderWriterLockSlim,MethodImplAttribute,WaitHandle常用同步机制的原理和使用场景。

 

volatile
只是C#的一个关键字,告诉编译器不能将声明的这个变量进行CPU内部缓存,只能在主内存中操作,类型有限制,volatile并不能实现真正的同步,因为它的操作级别只停留在变量级别,而不是原子级别。如果是在单处理器系统中,是没有任何问题的,变量在主存中没有机会被其他人修改,因为只有一个处理器,这就叫作processor Self-Consistency。但在多处理器系统中,可能就会有问题。 每个处理器都有自己的data cache,而且被更新的数据也不一定会立即写回到主存。所以可能会造成不同步,但这种情况很难发生,因为cache的读写速度相当快,flush的频率也相当高,只有在压力测试的时候才有可能发生,而且几率非常非常小。本质上说并非绝对的同步方法。

 

Interlocked
对于例如int变量等的原子操作,效率高,可靠性高,一般通过CPU的专用指令实现的锁住内存总线实现的。

 

lock
lock与Monitor本身是一致的,lock是做到了C#的关键字一级,是.net对象自身支持的的一种同步机制,对象中有相关的结构支持这种轻量级的线程同步,实现机制类似于CRITICAL_SECTION,但是CRITICAL_SECTION具有跨进程特性,而lock只能实现同一进程中的线程同步,在C#开发中很常用。

 

Mutex
是WIN32下的突变体内核对象的封装,类似于一间屋子只能进入一个人。是它的一个.net封装,效率比较低,由于突变体是一种windows内核对象,需要开销很大,但是支持跨进程,通过给Mutex命名的方式支持进程间同步,甚至可以跨服务器访问,是一种服务器之间同步的选择。Mutex的拥有者才能释放这个Mutex,其他进程不能释放,可能是考虑到安全问题。Mutex是一种基于线程调度的同步方式,控制的是线程的调度,实现了sleep,如果有信号可以通知内核线程调度程序调度等待线程。

 

Semaphore(Binary semaphore)
基于WIN32的Semaphore,也是一种基于线程调度,基本很类似于Mutex,与Mutex不同之处在于Semaphore允许多人进入同一间屋子,使用count计数来实现,当允许数量为1时叫做Binary semaphore,这时候就是基本和Mutex很类似的,但是没有Mutex拥有者一说,可由任何进程进行资源释放。

 

Spin lock
这是一个内核态概念。spin lock与semaphore的主要区别是spin lock是busy waiting,而semaphore是sleep。对于可以sleep的进程来说,busy waiting当然没有意义,CPU只是在那里空转而已,而且IRQL比较高,适合于等待时间比较短的场景。对于单CPU的系统,busy waiting当然更没意义(没有CPU可以释放锁),所有Spin lock只对多CPU才有意义,因此,只有多CPU的内核态非进程空间,才会用到spin lock。其实也就是类似mutex的作用,串行化对 critical section的访问。但是mutex不能保护中断的打断,也不能在中断处理程序中被调用。而spin lock也一般没有必要用于可以sleep的进程空间。幸好它是内核级的,如果是用户级的会很危险。


AutoResetEvent,ManualResetEvent  (Event)
这两种的实现都是基于WIN32的Event原理,同步事件有两种:AutoResetEvent 和 ManualResetEvent。它们之间唯一的不同在于,无论何时,只要 AutoResetEvent 激活线程,它的状态将自动从终止变为非终止。相反,ManualResetEvent 允许它的终止状态激活任意多个线程,只有当它的 Reset 方法被调用时才还原到非终止状态。


ReaderWriterLockSlim
这个也是lock的封装,对资源的访问方式有共享和独占方式,例如我们控制对某个资源读贡献或者写独占,那么这个类可以派上用场。

 

SynchronizationAttribute ,MethodImplAttribute
这两个属于类特性和方法的特性,标识某个类或方法是同步方法,本质上基于lock的实现。

 

WaitHandle
可以通过调用一种等待方法,如 WaitOne、WaitAny 或 WaitAll,让线程等待事件。System.Threading.WaitHandle.WaitOne 使线程一直等待,直到单个事件变为终止状态;System.Threading.WaitHandle.WaitAny 阻止线程,直到一个或多个指示的事件变为终止状态;System.Threading.WaitHandle.WaitAll 阻止线程,直到所有指示的事件都变为终止状态。当调用事件的 Set 方法时,事件将变为终止状态。WaitOne基于WaitSingleObject,WaitAny 或 WaitAll基于WaitmultipleObject,具体由后面参数来决定。WaitmultipleObject实现要比WaitSingleObject复杂的多,性能也不好,尽量少用。

 

【作者】:GUO Xingwang
【来源】:http://thriving-country.cnblogs.com/ 
     本文版权归作者和博客园共同所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。
Add your comment

18 条回复

  1. #1楼 伍华聪      2010-12-21 11:54
    不错,介绍的比较全面
     回复 引用 查看   
  2. #2楼 谭亮      2010-12-21 12:23
    很好
     回复 引用 查看   
  3. #3楼 横竖都溢      2010-12-21 12:25
    博客整体风格很不错,感觉比较舒服。文章内容也很全面,希望能有后续文章。
     回复 引用 查看   
  4. #4楼 Zhenway      2010-12-21 12:31
    不错,关于volatile补充一点
    有些内存数据会被设备更改(例如DMA),因此,即使是单处理器,某些情况下也要使用volatile
     回复 引用 查看   
  5. #5楼[楼主] GUO Xingwang      2010-12-21 12:46
    @Zhenway
    受教了!
     回复 引用 查看   
  6. #6楼[楼主] GUO Xingwang      2010-12-21 12:48
    @Zhenway
    谢谢你 让我想到了CPU和IO的并行!
     回复 引用 查看   
  7. #7楼 不仅仅是通用权限设计      2010-12-21 13:20
    再次跑过来推荐一下
     回复 引用 查看   
  8. #8楼 yunpeng      2010-12-21 14:18
     回复 引用 查看   
  9. #9楼 微生物      2010-12-21 15:05
    比msdn给力多了。
     回复 引用 查看   
  10. #10楼 LanceZhang      2010-12-21 15:18
    收藏,总结的非常好
     回复 引用 查看   
  11. #11楼 Daniel Xu      2010-12-21 16:40
    如果有例子简单介绍下就好了
     回复 引用 查看   
  12. #12楼[楼主] GUO Xingwang      2010-12-21 17:26
    @Daniel Xu
    这里有一个例子,还有源代码可下载
    http://www.cnblogs.com/Thriving-Country/archive/2010/08/21/1805508.html
     回复 引用 查看   
  13. #13楼 小AI      2010-12-21 18:40
    AutoResetEvent 和 ManualResetEvent这个东西可以实现生产者消费者模式!!感觉比较爽
     回复 引用 查看   
  14. #14楼 小AI      2010-12-21 18:41
    实例化的时候最好false
     回复 引用 查看   
  15. #15楼 小AI      2010-12-21 18:42
    set,wait,reset,温习了一下
     回复 引用 查看   
  16. #16楼[楼主] GUO Xingwang      2010-12-22 09:40
    @小AI
    对 经典的生产者和消费者问题通过这些可以搞定
     回复 引用 查看   
  17. #17楼 zhaohua_wang      2010-12-24 14:52
    总结全面,拜读了,感谢博主!
     回复 引用 查看   
  18. #18楼 NewSea.      2012-02-03 13:18
    学习.
     回复 引用 查看