C#中怎么使用lock关键字?有哪些注意事项?先看MSDN的定义:lock 关键字可以用来确保代码块完成运行,而不会被其他线程中断。这是通过在代码块运行期间为给定对象获取互斥锁来实现的。
假设线程A先执行,线程B稍微慢一点。线程A执行到lock语句,判断obj是否已申请了互斥锁, 判断依据是逐个与已存在的锁进行object.ReferenceEquals比较(此处未加证实),如果不存 在,则申请一个新的互斥锁,这时线程A进入lock里面了。 这时假设线程B启动了,而线程A还未执行完lock里面的代码。线程B执行到lock语句,检查到obj 已经申请了互斥锁,于是等待;直到线程A执行完毕,释放互斥锁,线程B才能申请新的互斥锁并执行 lock里面的代码。
接下来说一些该lock什么对象。 为什么不能lock值类型,比如lock(1)呢?lock本质上Monitor.Enter,Monitor.Enter会使值类型装箱, 每次lock的是装箱后的对象。lock其实是类似编译器的语法糖,因此编译器直接限制住不能lock值类型。 退一万步说,就算能编译器允许你lock(1),但是object.ReferenceEquals(1,1)始终返回false(因为 每次装箱后都是不同对象),也就是说每次都会判断成未申请互斥锁,这样在同一时间,别的线程照样能 够访问里面的代码,达不到同步的效果。同理lock((object)1)也不行。 那么lock("xxx")字符串呢?MSDN上的原话是: 锁定字符串尤其危险,因为字符串被公共语言运行库 (CLR)“暂留”。 这意味着整个程序中任何给定字符串 都只有一个实例,就是这同一个对象表示了所有运行的应用程序域的所有线程中的该文本。因此,只要在应用 程序进程中的任何位置处具有相同内容的字符串上放置了锁,就将锁定应用程序中该字符串的所有实例。 通常,最好避免锁定 public 类型或锁定不受应用程序控制的对象实例。例如,如果该实例可以被公开访问, 则 lock(this) 可能会有问题,因为不受控制的代码也可能会锁定该对象。这可能导致死锁,即两个或更多个线 程等待释放同一对象。出于同样的原因,锁定公共数据类型(相比于对象)也可能导致问题。而且lock(this) 只对当前对象有效,如果多个对象之间就达不到同步的效果。 lock(typeof(Class))与锁定字符串一样,范围太广了。
某些系统类提供专门用于锁定的成员。例如,Array 类型提供 SyncRoot。许多集合类型也提供 SyncRoot。 而自定义类推荐用私有的只读静态对象,比如: private static readonly object obj = new object(); 为什么要设置成只读的呢?这时因为如果在lock代码段中改变obj的值,其它线程就畅通无阻了,因为互斥锁的 对象变了,object.ReferenceEquals必然返回false。 |
原文参考:http://hi.baidu.com/yk%B1%B1%BC%AB%D0%C7/blog/item/fe3f8418b18f2361dbb4bd46.html
http://stackoverflow.com/questions/251391/why-is-lockthis-bad
semaphore 可用于进程间同步也可用于同一个进程间的线程同步。
semaphore 非常类似于mutex ,
共同点:semaphore和mutex都是内核对象,都可用于进程间的同步,并且都特别占用系统资源(线程的同步包括用户模式下的同步和内核模式下的同步,如果用内核对象来同步被保护的资源,系统需要从用户模式切换到内核模式,这个时间大概是1000个cpu周期)。
区别为:mutex只能由一个线程(进行)访问被保护的资源。semaphore 是一种带计数的mutex的锁定,可定义同时访问被保护的资源的线程数。
信号量有一个使用计数器,这个使用计数器,是信号量的最大资源计数和当前资源计数的差值。
信号量的规则如下:
a、如果当前资源计数大于0,那么信号量处于触发状态。
b、如果当前资源计数等于0,那么信号量处于未触发状态。
c、系统绝对不会让当前资源计数变为负数。
d、当前资源计数绝对不会大于最大最大资源计数