博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
泛型实现中没有正确lock引用类型的一个隐藏bug分析
阅读量:6573 次
发布时间:2019-06-24

本文共 1322 字,大约阅读时间需要 4 分钟。

最近看到文章的回复,让我想起一个真实发生的案例,下面就简单说说这个关于lock引用类型的一个不容易发现的隐藏缺陷。

某类库中的代码,封装了很简单的一个通用类,用于线程安全地执行某一种类型的特定方法,几行代码搞定:

public class ConcurrentObjectExecutor
where T : IDisposable, new() { public void Start() { T obj = new T(); lock (obj) { Console.WriteLine(obj.ToString()); //do sth } } }

 

设计这个类,估计本来主要是针对继承自特定接口的类型能够线程安全地执行某一个方法。如果泛型类型T为类(class),则程序运行没有任何问题,也能保证线程安全。

但是我们知道泛型约束只有继承自接口和new还远远不能保证T就是一个class,结构(struct)也可以继承接口,也可以new。比如自定义一个结构:

struct OrderMessger : IDisposable    {        public void Dispose()        {        }    }

 

下面的代码可以编译通过,运行时也不会抛出异常(如果不看上下文,多数调用者估计就这么让它过去,很可能成为今后一个潜在的隐藏很深的bug):

var executor = new ConcurrentObjectExecutor
(); executor.Start();

 

很显然,上面的泛型程序看上去是lock了一个结构,也就是锁定了一个值类型。但是我们知道,lock关键字指定的锁定对象必须是引用类型。上面的示例中,实际情况是将结构实例obj隐式转换成了一个引用对象实例(也就是装箱,每次装箱都生成了一个新的实例),这样的lock是毫无意义的,因为在多线程的条件下,线程争用的obj已经不是指定的同一个实例(的引用)。

比较搞笑的是,直接写下面的代码,编译时直接在lock语句上报告有错误(编译时检测真是帮了大忙,ms为什么不把泛型检测搞的更智能些?):

var obj = new OrderMessger();            lock (obj)            {            }

 

错误    1    “OrderMessger”不是 lock 语句要求的引用类型 

解决的方法也很简单,泛型约束在原来的基础上再限定必须是class即可。

最后总结下:类库设计中,越是通用的东西考虑的应用场景越要周到,其中线程安全是非常重要必不可少的一个环节,线程安全实现过程中,如果对多线程同步原语理解不够深刻,很可能设计出有潜在缺陷的实现,MSDN关于Thread Safe的调用和说明值得类库开发者深刻学习和借鉴。

转载地址:http://lrojo.baihongyu.com/

你可能感兴趣的文章
易语言 --什么情况下 用许可证
查看>>
项目总结:凡事预则立,不预则废!
查看>>
ORA-32004: obsolete and/or deprecated parameter(s)
查看>>
建属于自己的网站
查看>>
[linux] ubuntu 切换默认的/bin/sh
查看>>
Web Bench (网站压力测试工具)
查看>>
boost库之智能指针
查看>>
linux c/c++ GDB教程详解(转载)
查看>>
centos7下安装Python的pip
查看>>
华为HCIE 面试战报
查看>>
C++ 一些知名的库
查看>>
用busybox创建一个不足50M的Linux
查看>>
在redhat server 6 安装gcc-4.5.2
查看>>
我的友情链接
查看>>
自定义View Client 登录方式(一)
查看>>
rsync搭建使用
查看>>
一台服务器上同时运行多个MySQL
查看>>
cenOS+nginx+php+mysql (非一键包安装)
查看>>
优秀程序员不一定是优秀的软件设计师
查看>>
JS系列
查看>>