内容简介:如果你编写线程安全代码时为了省事儿直接不应该看看下面的两段代码。
如果你编写线程安全代码时为了省事儿直接 lock(this)
,或者早已听说不应该 lock(this)
,只是不知道原因,那么阅读本文可以帮助你了解原因。
原因
不应该 lock(this)
是因为你永远不知道别人会如何使用你的对象,永远不知道别人会在哪里加锁。于是稍不注意就可能死锁!
实例
看看下面的两段代码。
第一段是定义好的一个类,其中某个方法为了线程安全加了锁,但加锁的是 this
对象。
public class Foo { public void DoSafety() { lock (this) { // 执行一些线程安全的事情。 } } }
第二段代码使用了这个类的一个实例。为了响应放到了后台线程中,但为了线程安全,加了锁。
public class Bar { private readonly Foo _foo = new Foo(); public async void DouB_Walterlv() { lock (_foo) { await Task.Run(() => _foo.DoSafety()); } } }
仔细看看这段代码,如果 DouB_Walterlv
方法执行,会发生什么?
—— 死锁
在 DouB_Walterlv
方法中完全看不出来为什么死锁,只能进入到 DoSafety
中才发现试图 lock
的 this
对象刚刚在另一个线程被 lock (_foo)
了。
扩展
从以上的例子可以看出,不止是 lock (this)
会出现“难以捉摸”的死锁问题, lock
任何公开对象都会这样。
lock 公开的属性
public class Foo { public object SyncRoot { get; } = new object(); }
只要在 A 处 lock
这个对象的同时,在另一个线程调用了同样 lock
这个对象的 B 处的代码,必然死锁。
如果你试图实现某些接口中的 SyncRoot
属性,却遇到了上述矛盾(这样的写法不安全),那么可以阅读我的另一篇博客了解如何实现这样的“有问题”的接口:
lock 字符串
你可以定义一个私有的字符串,但你永远不知道这个字符串是否与其他字符串是同一个实例。因此这也是不安全的。
- .NET/C# 的字符串暂存池 - walterlv
- .NET/C# 编译期间能确定的相同字符串,在运行期间是相同的实例 - walterlv
- .NET/C# 编译期能确定的字符串会在字符串暂存池中不会被 GC 垃圾回收掉 - walterlv
lock 其他任何可能被其他对象获取的公开对象
比如 Type
对象,比如其他公共静态对象。
结论
所以,一旦你决定 lock
,那么这个对象请做成 private
。
以上所述就是小编给大家介绍的《为什么不应该公开用来同步的加锁对象?为什么不应该 lock(this)/lock(string) 或者 lock 任何非私...》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 顶会论文应不应该提交代码?应该,但不能强制
- 多人游戏的网络实现:帧同步和状态同步
- MySQL主从同步机制和同步延时问题追查
- 单元测试 – 我应该对不应该在函数中传递的数据(无效输入)进行单元测试吗?
- 智能合约事件应该这么用
- 研发职位到底应该怎么设置?
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。