C#Lock机制

定义:lock 确保当一个线程位于代码的临界区时,另一个线程不进入临界区。如果其他线程试图进入锁定的代码,则它将一直等待(即被阻止),直到该对象被释放。

Monitor 方法是静态的,不需要生成Monitor 类的实例就可以直接调用它们。在.NET Framework 中,每个对象都有一个与之关联的锁,对象可以得到并释放它以便于在任意时间只有一个线程可以访问对象实例变量和方法。

Lock语句可以说就是通过Monitor.Enter()和Monitor.Exit()实现的。

当Lock(lockInstance){}结构开始执行时调用Monitor.Enter(lockInstance)锁定lockInstance临界区,当该结构执行结束,调用monitor.Exit(lockInstance)释放lockInstance临界区。

原理:对于任何一个对象来说,它在内存中的第一部分放置的是所有方法的地址,第二部分放着一个索引,这个索引指向CLR中的SyncBlock Cache区域中的一个SyncBlock。当你执行Monitor.Enter(Object)时,如果object的索引值为负数,就从SyncBlock Cache中选取一个SyncBlock,将其地址放在object的索引中。这样就完成了以object为标志的锁定,其他的线程想再次进行Monitor.Enter(object)操作,将获得object的已经为正值的索引,然后就等待。直到索引变为负数,即调用Monitor.Exit(object)将索引变为负数,等待的线程开始执行。


注意:

1.lock不能锁定空值,但Null是不需要被释放的。
2.lock不能锁定string类型,虽然它也是引用类型的。因为字符串类型被CLR“暂留”。即整个程序中任何给定字符串都只有一个实例,具有相同内容的字符串都代表着同一个实例。因此,只要在应用程序进程中的任何位置处具有相同内容的字符串上放置了锁,就将锁定应用程序中与该字符串具有相同内容的字符串。因此,最好锁定不会被暂留的私有或受保护成员。

实例:

测试结果 :

可以看出,str01和str02是两个string类型的变量,但是当lock(str01)之后,str02变成了临界区被锁定导致th2中lock(){}体处于等待状态。这就对应了只要在应用程序进程中的任何位置处具有相同内容的字符串上放置了锁,就将锁定应用程序中与该字符串具有相同内容的字符串这句话。
3.lock锁定的对象是一个程序块的内存边界
4.值类型不能被lock,因为前文标红字的“对象被释放”,值类型不是引用类型的

5.lock就避免锁定public 类型或不受程序控制的对象。
例如,如果该实例可以被公开访问,则 lock(this) 可能会有问题,因为不受控制的代码也可能会锁定该对象。这可能导致死锁,即两个或更多个线程等待释放同一对象。出于同样的原因,锁定公共数据类型(相比于对象)也可能导致问题。
使用lock(this)的时候,类的成员变量的值可能会被不在临界区的方法改值了

应用场景:经常会应用于防止多线程操作导致公用变量值出现不确定的异常,用于确保操作的安全性。

实例:

测试结果:

可以看出,当线程th执行lock(stu)之后,stu成为临界区被锁定。其他线程等待该lock(){}执行完成stu被释放后,开始执行。 

另外,由于我是学习Unity游戏开发的,这里插入一句,在Unity中是不允许在子线程中访问Unity的一些属性例如Transfrom等。我猜测这是因为防止因为子线程的使用不当,导致Unity底层数据的错乱。所以Unity直接使用Lock机制阻止用户直接在子线程中使用Unity的一些特定属性。

发布了13 篇原创文章 · 获赞 9 · 访问量 8257

猜你喜欢

转载自blog.csdn.net/qq_39025293/article/details/84655433
今日推荐