为什么C#编译器处理lock语句时会拷贝一份临时锁变量?
C# lock语句生成临时锁对象变量的原因
编译器新增临时变量存储锁对象,核心是为了保证Monitor.Enter和Monitor.Exit操作的始终是同一个对象实例,避免出现锁泄漏、错误释放其他对象等严重问题,具体原因如下:
- 避免锁变量被修改导致锁逻辑异常
如果直接使用原变量mylock,一旦在Monitor.Enter执行完成后到finally执行前的这段时间里,mylock的指向被修改(不管是锁块内部代码修改,还是其他并发线程修改),finally块里的Monitor.Exit操作就会作用在修改后的新对象上:- 你实际持有的旧锁对象永远不会被释放,造成锁泄漏,后续所有尝试获取该旧对象锁的代码都会永久阻塞
- 如果新对象已经被其他线程持有锁,你尝试释放不属于自己的锁还会抛出异常
举个会触发问题的代码示例:
public object mylock = new object(); void BadCaseTest() { bool lockTaken = false; try { Monitor.Enter(mylock, ref lockTaken); // 此处修改了原锁变量的指向 mylock = new object(); } finally { if (lockTaken) { // 这里释放的是新创建的对象,不是实际持有的旧锁对象 Monitor.Exit(mylock); } } }
用临时变量存储进入lock语句时的锁对象后,不管后续原变量mylock怎么修改,Enter和Exit都是作用在同一个对象上,完全规避了上述问题。
- 严格符合C#语言规范的语义定义
C#语言规范明确规定:lock语句的锁定对象是进入lock块时,对锁定表达式求值得到的结果实例,后续该表达式对应的变量发生任何变更都不影响本次lock的锁定对象。临时变量的实现就是对该语义的严格落地。
内容的提问来源于stack exchange,提问作者leadingwerido
相关产品推荐
相关产品推荐

