lock 的目的很明确:就是不想让别人使用这段代码,体现在多线程情况下,只允许当前线程执行该代码区域,其他线程等待直到该线程执行结束;这样可以多线程避免同时使用某一方法造成数据混乱。
lock 关键字可以用来确保代码块完成运行,而不会被其他线程中断。它可以把一段代码定义为互斥段(critical section),互斥段在一个时刻内只允许一个线程进入执行,而其他线程必须等待。
一般定义如下:
private static readonly object locker = new object(); lock(locker) {//互斥段begin //Your Code... }//互斥段end 其相当于如下代码: //System.Threading.Moniter.enter(Object),Moniter类提供同步访问//对象的机制,enter方法在指定对象上获取排他锁,之后其他线程不能再//次获取,直到释放 Monitor.enter(locker); {//互斥段begin //Your Code... }//互斥段end Monitor.exit(locker); //exit(Object)释放指定对象上的排他锁。
//lock 语句的表达式必须表示一个引用类型的值。永远不会为 lock 语句中的表达式执行隐式装箱转换,因此,如果该表达式表示的是一个值类型的值,则会导致一个编译时错误。
MSDN给出:我们lock的一般是对象,不是数值和字符串。
1、为什么不能lock值类型
比如lock(1)呢?lock本质上Monitor.Enter,Monitor.Enter会使值类型装箱,每次lock的是装箱后的对象。lock 其实是类似编译器的语法糖,因此编译器直接限制住不能lock值类型。退一万步说,就算能编译器允许你lock(1),但是 object.ReferenceEquals(1,1)始终返回false(因为每次装箱后都是不同对象),也就是说每次都会判断成未申请互斥锁,这样 在同一时间,别的线程照样能够访问里面的代码,达不到同步的效果。同理lock((object)1)也不行。
2、Lock字符串
那么lock("xxx")字符串呢?MSDN上的原话是:
锁定字符串尤其危险,因为字符串被公共语言运行库 (CLR)“暂留”。 这意味着整个程序中任何给定字符串都只有一个实例,同一个对象表示了所有运行的应用程序域的所有线程中的该文本。因此,只要在应用程序进程中的任何 位置处具有相同内容的字符串上放置了锁,就将锁定应用程序中该字符串的所有实例。
3、MSDN推荐的Lock对象
通常,最好避免锁定 public 类型或锁定不受应用程序控制的对象实例。例如,如果该实例可以被公开访问,则 lock(this) 可能会有问题,因为不受控制的代码也可能会锁定该对象。这可能导致死锁,即两个或更多个线程等待释放同一对象。出于同样的原因,锁定公共数据类型(相比于 对象)也可能导致问题。
而且lock(this)只对当前对象有效,如果多个对象之间就达不到同步的效果。
而自定义类推荐用私有的只读静态对象,比如:
private static readonly object obj = new object();
为什么要设置成只读的呢?这是因为如果在lock代码段中改变obj的值,其它线程就畅通无阻了,因为互斥锁的对象变了,object.ReferenceEquals必然返回false。
转载 :https://blog.csdn.net/smooth_tailor/article/details/52411359
转载 :https://www.cnblogs.com/Janzen/p/5475519.html
查看 :https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/lock-statement