为什么不应该公开用来同步的加锁对象?为什么不应该 lock(this)/lock(string) 或者 lock 任何非私...

栏目: IT技术 · 发布时间: 4年前

内容简介:如果你编写线程安全代码时为了省事儿直接不应该看看下面的两段代码。

如果你编写线程安全代码时为了省事儿直接 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 中才发现试图 lockthis 对象刚刚在另一个线程被 lock (_foo) 了。

扩展

从以上的例子可以看出,不止是 lock (this) 会出现“难以捉摸”的死锁问题, lock 任何公开对象都会这样。

lock 公开的属性

public class Foo
{
    public object SyncRoot { get; } = new object();
}

只要在 A 处 lock 这个对象的同时,在另一个线程调用了同样 lock 这个对象的 B 处的代码,必然死锁。

如果你试图实现某些接口中的 SyncRoot 属性,却遇到了上述矛盾(这样的写法不安全),那么可以阅读我的另一篇博客了解如何实现这样的“有问题”的接口:

lock 字符串

你可以定义一个私有的字符串,但你永远不知道这个字符串是否与其他字符串是同一个实例。因此这也是不安全的。

lock 其他任何可能被其他对象获取的公开对象

比如 Type 对象,比如其他公共静态对象。

结论

所以,一旦你决定 lock ,那么这个对象请做成 private


以上所述就是小编给大家介绍的《为什么不应该公开用来同步的加锁对象?为什么不应该 lock(this)/lock(string) 或者 lock 任何非私...》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

算法之美

算法之美

左飞 / 电子工业出版社 / 2016-3 / 79.00元

《算法之美——隐匿在数据结构背后的原理(C++版)》围绕算法与数据结构这个话题,循序渐进、深入浅出地介绍了现代计算机技术中常用的40 余个经典算法,以及回溯法、分治法、贪婪法和动态规划等算法设计思想。在此过程中,《算法之美——隐匿在数据结构背后的原理(C++版)》也系统地讲解了链表(包括单向链表、单向循环链表和双向循环链表)、栈、队列(包括普通队列和优先级队列)、树(包括二叉树、哈夫曼树、堆、红黑......一起来看看 《算法之美》 这本书的介绍吧!

SHA 加密
SHA 加密

SHA 加密工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具