为什么不允许锁定(),但允许使用Monitor.Enter()?
对于以下代码,我得到编译时错误,*
‘int’不是lock语句所要求的引用类型
int i = 0; lock(i);
但没有错误:
int i = 0; Monitor.Enter(i);
据我所知,由于拳击造成的并发症,不应使用值类型进行锁定。 但是,为什么它与Monitor一起使用。
原因是lock是一种语言结构,编译器选择在表达式上强加额外的语义。 Monitor.Enter只是一个方法调用,C#编译器不会以任何方式调用特殊情况,因此它会通过正常的重载解析和装箱。
你绝对不应该在int
上使用Monitor.Enter
。 它工作的原因是因为int
是盒装的,所以除非你存储对盒装值的引用,否则你将锁定一个临时对象,这意味着你不能在没有exception的情况下调用Monitor.Exit
。
建议的锁定方法是创建一个private readonly object
并锁定它。 对于静态方法,您可以使用private static object
。
编译器的规范定义了锁的行为,如下所示 :
lock语句表达式的编译时类型应为reference-type或> type参数(第25.1.1节),已知为引用类型。 表达式的编译时类型表示值类型是编译时错误。
然后,只要它编译,它就定义了它的等价物
由于Monitor.Exit只是一个没有任何约束的方法调用,它不会阻止编译器自动装箱int并继续其快乐(和非常)错误的方式。
lock
不仅仅是语法糖,就像foreach
不仅仅是语法糖一样。 生成的IL变换不会呈现给代码的其余部分,就好像这是已编写的内容。
在foreach
中修改迭代变量是违法的(尽管在结果输出代码中IL级别没有任何内容可以阻止这种情况)。 在锁定中,编译器会阻止编译时已知值类型,尽管导致IL不关心这一点。
作为旁白:
从理论上讲,编译器可以“熟悉”这个(和其他)方法的深入了解,以便发现这种情况的明显情况,但从根本上说,在编译时总是不可能发现这一点(考虑从另一个方法传入的对象,assembly或通过reflection)如此困扰以发现任何此类情况可能会适得其反。
编译器对API内部的了解越多,如果您希望将来更改API,您将遇到的问题就越多。
例如,可以添加Monitor.Enter()的重载,该重载接受int并锁定在与int值关联的进程范围的互斥锁上。
这将符合监视器的规范(即使它可能是可怕的),但是会对旧编译器造成大量问题,仍然可以快速地阻止已经合法的操作。
出于好奇,你对变量’i’做了什么需要它被锁定? 如果所有操作都是增量或其他内容,则使用Interlocked类可能更有效:
Interlocked.Increment(i); // i++ in a thread safe manner
Interlocked类是.NET提供的最轻量级线程同步工具,对于简单的增量,减量,读取或交换,它是最佳选择。
如果您尝试同步一个行为块,那么我只需创建一个可用作同步根的对象:
object syncRoot = new object(); // ... lock(syncRoot) { // put synced behavior here }
我想这是因为Monitor.Enter()
是一个方法调用,所以编译器自动执行装箱,而lock()
是一个语法元素,因此编译器可以检查并在值类型上抛出错误。
上述就是C#学习教程:为什么不允许锁定(),但允许使用Monitor.Enter()?分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—计算机技术网(www.ctvol.com)!
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/cdevelopment/1031880.html