编辑:我在问两个线程在没有正确同步的情况下同时访问相同数据时会发生什么情况(在此编辑之前,该点未明确表达)。
我对C#编译器和JIT编译器执行的优化有疑问。
考虑以下简化示例:
class Example {
private Action _action;
private void InvokeAction() {
var local = this._action;
if (local != null) {
local();
}
}
}
请在示例中忽略读取_action
可能会产生缓存和过期值,因为没有volatile说明符或任何其他同步。这不是重点:)
是否允许编译器(或实际上是运行时的抖动)优化对局部变量的赋值,而是从内存中读取_action
两次:
class Example {
private Action _action;
private void InvokeAction() {
if (this._action != null) {
this._action(); // might be set to null by an other thread.
}
}
}
当字段NullReferenceException
被并发分配设置为_action
时,可能会抛出null
。
当然,在这个例子中,这样的“优化”没有任何意义,因为将值存储在寄存器中并因此使用局部变量会更快。但是在更复杂的情况下,是否可以保证按预期工作而不重新读取内存中的值?
答案 0 :(得分:7)
我会(部分地)说与mgronber相反:-) Aaaah ......最后我说的是同样的事情......只是我引用了一篇文章:-(我会给他一个1。
这是ECMA规范下的法律优化,但它是.NET> = 2.0“规范”下的非法优化。
来自Understand the Impact of Low-Lock Techniques in Multithreaded Apps
请阅读此处 Strong Model 2: .NET Framework 2.0
第2点:
无法进行读写操作。
以下解释:
然而,该模型不允许引入读取,因为这意味着从内存中重新获取值,而低锁代码内存可能会发生变化。
但请注意同一页下的Technique 1: Avoiding Locks on Some Reads
下的注释在使用ECMA模型的系统中,还有一个细微之处。甚至 如果只有一个内存位置被提取到局部变量中 local多次使用,每次使用可能有不同的值! 这是因为ECMA模型允许编译器消除本地 变量并重新获取每次使用的位置。如果有更新 同时发生,每次获取可能有不同的值。这个 使用volatile声明可以抑制行为,但是问题 很容易错过。
如果你是在Mono下写作,你应该被告知至少在2008年之前它正在使用ECMA内存模型(或者他们在mailing list中写道)
答案 1 :(得分:2)
根据ECMA规范中定义的内存模型进行合法优化。如果_action是volatile,那么内存模型将保证该值只读一次,因此不会发生这种优化。
但是,我认为当前微软的CLR实现并没有优化局部变量。