泛型实现中没有正确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中值类型和引用类型的区别
[定义] 引用类型表示你操作的数据是同一个,也就是说当你传一个参数给另一个方法时,你在另一个方法中改变这个变量的值,那么调用这个方法是传入的变量的值也将改变. 值类型表示复制一个当前变量传给方法,当你 ...
随机推荐
- css中图片等比例缩放
li img{ display: inline-block; max-height: 60px; max-width: 60px; vertical-align: middle; }
- (python) 标准模块sys和os的使用
一.sys模块 包含了系统的相关的功能.我们来学习sys.argv,它包含命令行参数. 例子:定义了一个add函数,用来实现两个整数的相加. #! coding=utf-8 # usersys.py ...
- android的单击监听事件
Button button = (Button) findViewById(R.id.button1); //1.直接new出来 button.setOnClickListener(new View. ...
- memcached总结
Memcached说明文档 Memcached是什么? Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象来减少读取数据库的次数 ...
- 解决拖拽有内容的div的bug和兼容问题
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 31.0px Consolas; color: #2b7ec3 } p.p2 { margin: 0.0px ...
- linux下解压被分割的zip文件
形如被分割的一系列文件:linux.z01, linux.z02, linux.z03, linux.zip 直接右键解压是不行的. 首先合并文件:cat linux.* > linux_all ...
- gruntJs篇之connect+watch自动刷新
grunt很强大,可以帮我我们解决很多繁琐的操作,虽然刚接触不久,但依然感受到其强大之处,这篇记录一下通过grunt.js实现事实刷新页面, 省去了编码 -> 保存 -> F5..F5.. ...
- 测试文档锁:doc.LockDocument()
/// <summary> /// 总结:用到DocumentManager.Open(filePath)时,如果是ForWrite,就需要用到lock文档锁. /// </summ ...
- 重编译Linux命令源代码
转: http://blog.csdn.net/endoresu/article/details/6967435 以su命令为例. 查看su命令的路径: # which su /bin/su 查看su ...
- Android中 int 和 String 互相转换的多种方法
1 如何将字串 String 转换成整数 int? A. 有两个方法: 1). int i = Integer.parseInt([String]); 或 i = Integer.parseInt([ ...