泛型实现中没有正确lock引用类型的一个隐藏bug分析
最近看到这篇文章dotNetDR_的回复,让我想起一个真实发生的案例,下面就简单说说这个关于lock引用类型的一个不容易发现的隐藏缺陷。
某类库中的代码,封装了很简单的一个通用类,用于线程安全地执行某一种类型的特定方法,几行代码搞定:
public class ConcurrentObjectExecutor<T> 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<OrderMessger>();
executor.Start();
很显然,上面的泛型程序看上去是lock了一个结构,也就是锁定了一个值类型。但是我们知道,lock关键字指定的锁定对象必须是引用类型。上面的示例中,实际情况是将结构实例obj隐式转换成了一个引用对象实例(也就是装箱,每次装箱都生成了一个新的实例),这样的lock是毫无意义的,因为在多线程的条件下,线程争用的obj已经不是指定的同一个实例(的引用)。
比较搞笑的是,直接写下面的代码,编译时直接在lock语句上报告有错误(编译时检测真是帮了大忙,ms为什么不把泛型检测搞的更智能些?):
var obj = new OrderMessger();
lock (obj)
{
}
错误 1 “OrderMessger”不是 lock 语句要求的引用类型
解决的方法也很简单,泛型约束在原来的基础上再限定必须是class即可。
最后总结下:类库设计中,越是通用的东西考虑的应用场景越要周到,其中线程安全是非常重要必不可少的一个环节,线程安全实现过程中,如果对多线程同步原语理解不够深刻,很可能设计出有潜在缺陷的实现,MSDN关于Thread Safe的调用和说明值得类库开发者深刻学习和借鉴。
泛型实现中没有正确lock引用类型的一个隐藏bug分析的更多相关文章
- 【旧文章搬运】PsVoid中IrpCreateFile函数在Win7下蓝屏BUG分析及解决
原文发表于百度空间,2010-04-05========================================================================== 这也许是我 ...
- cocos2d-x在android中响应返回键编译报错的bug分析
先看一段代码如何在Android中加入返回按键的响应 <span style="font-size:18px;">自己派生CCKeypadDelegate的子类,然后注 ...
- 最近调试HEVC中码率控制, 发现HM里面一个重大bug
最近调试HEVC中码率控制, 发现里面一个重大bug! 码率控制中有这么一个函数: Int TEncRCGOP::xEstGOPTargetBits( TEncRCSeq* encRCSeq, Int ...
- default 关键字泛型代码中的默认关键字(C# 编程指南)
在泛型类和泛型方法中产生的一个问题是,在预先未知以下情况时,如何将默认值分配给参数化类型 T: T 是引用类型还是值类型. 如果 T 为值类型,则它是数值还是结构. 给定参数化类型 T 的一个变量 t ...
- Unity 查找泛型List中的相同与不同数据
Unity查找泛型集合中的不同数据 本文提供全流程,中文翻译. Chinar 坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 -- 高分辨率用户请根据需求调整网页缩放比例) Chinar -- ...
- C#只能lock 引用类型的值 (转载)
Lock: C#只能lock 引用类型的值,如果lock一个int, bool,编译器会报错. 当一个互斥锁已被占用时,在同一线程中执行的代码仍可以获取和释放该锁.但是,在其他线程 ...
- Java 理论和实践: 了解泛型 识别和避免学习使用泛型过程中的陷阱
Brian Goetz (brian@quiotix.com), 首席顾问, Quiotix 简介: JDK 5.0 中增加的泛型类型,是 Java 语言中类型安全的一次重要改进.但是,对于初次使用泛 ...
- Java并发指南4:Java中的锁 Lock和synchronized
Java中的锁机制及Lock类 锁的释放-获取建立的happens before 关系 锁是java并发编程中最重要的同步机制.锁除了让临界区互斥执行外,还可以让释放锁的线程向获取同一个锁的线程发送消 ...
- java中值类型和引用类型的区别
[定义] 引用类型表示你操作的数据是同一个,也就是说当你传一个参数给另一个方法时,你在另一个方法中改变这个变量的值,那么调用这个方法是传入的变量的值也将改变. 值类型表示复制一个当前变量传给方法,当你 ...
随机推荐
- html5实现摇一摇功能
原理:使用DeviceMotion实现,关于DeviceMotion介绍可以查看 https://developer.mozilla.org/en-US/docs/Web/Reference/Even ...
- 对button或radiobutton制作样式
制作样式在drawable中进行 (cheakbox)在样式中点击变换图片,<selector> <item android:state_checked="true&quo ...
- 2.Nginx优化
[教程主题]:Nginx优化 [课程录制]: 创E [主要内容] Nginx 优化 nginx介绍 Nginx是俄罗斯人编写的十分轻量级的HTTP服务器,Nginx,它的发音为"engine ...
- java线程池初步理解
多线程基础准备 进程:程序的执行过程,持有资源和线程 线程:是系统中最小的执行单元,同一个进程可以有多个线程,线程共享进程资源 线程交互(同步synchronized):包括互斥和协作,互斥通过对象锁 ...
- Fire
Fire 分析: 首先,明确题意:b1,b2,--,bn 交换为b2,--,bn,b1,但这并不是意味着只能从b1开始交换,(这点从样例中可以看出),并且也不意味着交换的必须是连续的一串,可以是几个单 ...
- MATLAB cvx 工具包使用
一个例子 m = ; n = ; p = ; A = randn(m,n); b = randn(m,); C = randn(p,n); d = randn(p,); e = rand; cvx_b ...
- Sass的基本运算(转载)
转载于:http://www.cnblogs.com/Medeor/p/4966952.html Sass中的基本运算 一.加法 在 CSS 中能做运算的,到目前为止仅有 calc() 函数可行.但在 ...
- Firebird数据库的Select语句
select first 10 skip 8 * from t_data //跳过前8行不要,取10行,即取第9行到18行共10行 select first 10 * from t_data ...
- :尝试加载 Oracle 客户端库时引发 BadImageFormatException。如果在安装 32 位 Oracle 客户端组件的情况下以 64 位模式运行,将出现此问题。 ---> System.BadImageFormatException: 试图加载格式不正确的程序。
iis有程序池有一个属性 是否启用32位应用程序默认是true的特别注意windows2008 服务器系统iis默认的是true 这样发布一下也是必须要做的
- [XAF] How to hide individual navigation items and groups for certain users
https://www.devexpress.com/Support/Center/Example/Details/E380