对于下面的代码我得到一个编译时错误,*为什么不允许锁定(<integer var>),但允许Monitor.Enter(<integer var>)?
“诠释”不是引用类型 通过lock语句
int i = 0;
lock(i);
但对于这个没有任何错误要求:
int i = 0;
Monitor.Enter(i);
我知道值类型不应该用于锁定,因为由于du e拳击。但是,那么为什么它与Monitor一起工作。
对于下面的代码我得到一个编译时错误,*为什么不允许锁定(<integer var>),但允许Monitor.Enter(<integer var>)?
“诠释”不是引用类型 通过lock语句
int i = 0;
lock(i);
但对于这个没有任何错误要求:
int i = 0;
Monitor.Enter(i);
我知道值类型不应该用于锁定,因为由于du e拳击。但是,那么为什么它与Monitor一起工作。
为什么锁是一种语言结构,编译器选择在表达式上施加额外的语义。 Monitor.Enter只是一个方法调用,C#编译器不会以任何方式特殊处理调用,因此它会经历正常的重载解析和装箱。
我会说这是因为Monitor.Enter()
是一个方法调用,所以编译器自动执行拳,而lock()
是一个语法元素,因此编译器可以检查和值类型抛出一个错误。
您绝对不能在int
上使用Monitor.Enter
。它的工作原因是因为int
被装箱,所以除非您存储对装箱值的引用,否则您将锁定临时对象,这意味着您不能在没有发生异常的情况下调用Monitor.Exit
。
推荐的锁定方式是创建一个private readonly object
并对其进行锁定。对于静态方法,您可以使用private static object
。
只是出于好奇,你在做什么与变量'我',需要它被锁定?这可能是更有效地使用Interlocked类,如果你所有的做的是增量或东西:
Interlocked.Increment(i); // i++ in a thread safe manner
互锁类是重量最轻的线程同步工具.NET提供,以及简单的递增,递减,读或交换,这是最好的选择。
如果你试图行为的块同步,那么我会简单地创建可被用作同步根的对象:
object syncRoot = new object();
// ...
lock(syncRoot)
{
// put synced behavior here
}
纯粹出于兴趣......我知道联锁班。谢谢。 – Sandbox 2009-08-25 18:11:59
用于编译器中的规范定义the behaviour of lock like so:
锁语句的表达式的编译时类型应该是已知为引用类型的引用类型或类型参数(第25.1.1节)。表达式的 编译时类型表示值类型,这是编译时错误。
然后,它定义了它相当于,只要它编译
由于监视器。退出只是一个没有任何约束的方法调用,它不会阻止编译器自动装入int并继续其快乐(而且非常)错误的方式。
lock
不是简单的语法糖以相同的方式foreach
不是简单的语法糖。所得到的IL变换是而不是呈现给代码的其余部分,就好像那是写过的那样。
在foreach
修改迭代变量是非法的(尽管在结果输出代码中没有任何IL级别会阻止这一点)。在锁中,编译器会阻止编译时间已知的值类型,尽管由此产生的IL不关心这一点。
顺便说一句:
理论上编译器可以“祝福”与此(等)方法成竹在胸,使其看上这种情况出现明显的情况下,但根本上是不可能总是在发现这一点编译时间(考虑从另一个方法,程序集或通过反射传入的对象),因此很难发现任何此类实例可能会产生反效果。
编译器对API的内部知道得越多,如果您希望将来改变API,您将遇到的问题也越多。
例如,可以添加一个Monitor.Enter()的重载,它可以添加一个int并将其锁定在与int值关联的进程宽互斥量上。
这将符合监视器的规格(尽管它可能会很可怕),但会对较老的编译器造成严重问题,仍然会快速地阻止已成为合法的操作。
很好的答案。谢谢。 – Sandbox 2009-08-25 18:21:09
我不认为有任何检查。 不是锁只是用try块中的Monito.Enter和finally块中的Monitor.Exit进行扩展。 – Sandbox 2009-08-25 18:04:12
不,它被扩展到,但首先编译器点,如果你是愚蠢的道路上... – ShuggyCoUk 2009-08-25 18:05:29