当前位置: 首页 > news >正文

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

如果你编写线程安全代码时为了省事儿直接 lock(this),或者早已听说不应该 lock(this),只是不知道原因,那么阅读本文可以帮助你了解原因。


本文内容

    • 原因
    • 实例
    • 扩展
      • lock 公开的属性
      • lock 字符串
      • lock 其他任何可能被其他对象获取的公开对象
    • 结论

原因

不应该 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 属性,却遇到了上述矛盾(这样的写法不安全),那么可以阅读我的另一篇博客了解如何实现这样的“有问题”的接口:

  • 为什么实现 .NET 的 ICollection 集合时需要实现 SyncRoot 属性?如何正确实现这个属性?

lock 字符串

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

  • .NET/C# 的字符串暂存池 - walterlv
  • .NET/C# 编译期间能确定的相同字符串,在运行期间是相同的实例 - walterlv
  • .NET/C# 编译期能确定的字符串会在字符串暂存池中不会被 GC 垃圾回收掉 - walterlv

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

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

结论

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


我的博客会首发于 https://blog.walterlv.com/,而 CSDN 会从其中精选发布,但是一旦发布了就很少更新。

如果在博客看到有任何不懂的内容,欢迎交流。我搭建了 dotnet 职业技术学院 欢迎大家加入。

知识共享许可协议

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名吕毅(包含链接:https://walterlv.blog.csdn.net/),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系。

相关文章:

  • WPF 中如何创建忽略 DPI 属性的图片
  • .NET 中选择合适的文件打开模式(CreateNew, Create, Open, OpenOrCreate, Truncate, Append)
  • Win32 方法 CreateFile 中选择合适的文件打开模式(CREATE_NEW, CREATE_ALWAYS, OPEN_EXISTING, OPEN_ALWAYS, TRUNCATE_EXI
  • 使用 Kestrel 为你的 ASP.NET Core 服务添加 https 支持
  • 三种方法为 ASP.NET Core 对外服务添加 https 支持(kestrel / frp / nginx)
  • 为 ASP.NET Core 程序制作 URL 的 301/302 跳转
  • 使用 Frp 为你的 Web 服务添加 https 支持
  • 全民 https!使用 FreeSSL 申请免费的 https 证书
  • 如何设置 ASP.NET Core 程序监听的 IP 和端口
  • 收集的 Linux VPS 在线重装系统脚本
  • Linux 系统根目录下的文件夹
  • 修复 Windows 10 设置界面里面混乱的语言翻译
  • .NET 程序如何获取图片的宽高(框架自带多种方法的不同性能)
  • 用命令行执行 .NET 单元测试时,如何仅执行符合某些条件的单元测试
  • WPF 中如何绑定附加属性?XAML 中记得加括号,C# 中记得不能用字符串
  • 【108天】Java——《Head First Java》笔记(第1-4章)
  • 03Go 类型总结
  • es6
  • httpie使用详解
  • HTTP传输编码增加了传输量,只为解决这一个问题 | 实用 HTTP
  • Java编程基础24——递归练习
  • JS基础篇--通过JS生成由字母与数字组合的随机字符串
  • LintCode 31. partitionArray 数组划分
  • open-falcon 开发笔记(一):从零开始搭建虚拟服务器和监测环境
  • python大佬养成计划----difflib模块
  • Redash本地开发环境搭建
  • Vue2 SSR 的优化之旅
  • 番外篇1:在Windows环境下安装JDK
  • 提醒我喝水chrome插件开发指南
  • 想写好前端,先练好内功
  • 【云吞铺子】性能抖动剖析(二)
  • MPAndroidChart 教程:Y轴 YAxis
  • 好程序员大数据教程Hadoop全分布安装(非HA)
  • # 数据结构
  • (2021|NIPS,扩散,无条件分数估计,条件分数估计)无分类器引导扩散
  • (Arcgis)Python编程批量将HDF5文件转换为TIFF格式并应用地理转换和投影信息
  • (C语言)求出1,2,5三个数不同个数组合为100的组合个数
  • (Matalb回归预测)PSO-BP粒子群算法优化BP神经网络的多维回归预测
  • (阿里云在线播放)基于SpringBoot+Vue前后端分离的在线教育平台项目
  • (第61天)多租户架构(CDB/PDB)
  • (附源码)spring boot校园拼车微信小程序 毕业设计 091617
  • (附源码)springboot家庭财务分析系统 毕业设计641323
  • (附源码)springboot炼糖厂地磅全自动控制系统 毕业设计 341357
  • (附源码)SSM环卫人员管理平台 计算机毕设36412
  • (七)c52学习之旅-中断
  • (实测可用)(3)Git的使用——RT Thread Stdio添加的软件包,github与gitee冲突造成无法上传文件到gitee
  • (转载)(官方)UE4--图像编程----着色器开发
  • .NET 3.0 Framework已经被添加到WindowUpdate
  • .net framework profiles /.net framework 配置
  • .net 发送邮件
  • .net反编译工具
  • .net经典笔试题
  • .NET企业级应用架构设计系列之技术选型
  • @param注解什么意思_9000字,通俗易懂的讲解下Java注解
  • [\u4e00-\u9fa5] //匹配中文字符