Son*_*oul 9 c# multithreading .net-4.0
触发事件时避免竞争条件(在多线程应用程序中)的常见做法是:
EventHandler<EventArgs> temp = SomeEvent;
if (temp != null) temp(e);
"Remember that delegates are immutable and this is why this technique works in theory. However, what a lot of developers don't realize is that this code could be optimized by the compiler to remove the local temp variable entirely. If this happens, this version of the code is identical to the first version, so a NullReferenceException is still possible."
Run Code Online (Sandbox Code Playgroud)
问题(根据书中)是"编译器可以优化此代码以完全删除本地临时变量.如果发生这种情况,此版本的代码与第一个版本相同,因此仍然可以使用NullReferenceException"
根据CLR通过C#,这是强制编译器复制事件指针的更好方法.
virtual void OnNewMail(NewMailEventArgs e)
{
EventHandler<NewMailEventArgs> temp =
Interlocked.CompareExchange(ref NewMail, null, null);
if (temp != null)
temp(this, e);
}
Run Code Online (Sandbox Code Playgroud)
这里,如果它为null,则CompareExchange将NewMail引用更改为null,如果它不为null,则不会更改NewMail.换句话说,CompareExchange根本不会更改NewMail中的值,但它确实以原子,线程安全的方式返回NewMail中的值.里希特,杰弗里(2010-02-12).CLR通过C#(第265页).OReilly Media - A. Kindle版.
我在.Net 4.0框架上,并不确定这可能如何工作,因为Interlocked.CompareExchange需要对位置的引用,而不是对事件的引用.
书中有错误,或者我误解了它.有没有人实现过这种方法?或者有更好的方法来预防这里的竞争条件?
UPDATE
这是我的错误,iterlocked代码工作.我刚刚指定了错误的转换,但根据Bradley(下文),在.net 2.0和Windows上没有必要.
不允许编译器(或JIT)对其进行优化if/temp(在CLR 2.0及更高版本中); 在CLR 2.0内存模型不允许读取来自待引入的堆(规则#2).
因此,MyEvent第二次无法阅读; temp必须在if声明中读取值.
有关此情况的详细讨论,请参阅我的博客文章,并解释为什么标准模式没问题.
但是,如果您运行的是非Microsoft CLR(例如,单声道),它不提供CLR 2.0内存模型保证(但仅遵循ECMA内存模型),或者您在Itanium上运行(其中有一个众所周知的弱硬件内存模型),你需要像Richter这样的代码来消除潜在的竞争条件.
关于你的问题Interlocked.CompareExchange,语法public event EventHandler<NewMailEventArgs> NewMail只是C#语法糖,用于声明类型的私有字段EventHandler<NewMailEventArgs>和具有add和remove方法的公共事件.该Interlocked.CompareExchange调用读取私有EventHandler<NewMailEventArgs>字段的值,因此该代码的编译和工作正如Richter所描述的那样; 它在Microsoft CLR中是不必要的.
| 归档时间: |
|
| 查看次数: |
2159 次 |
| 最近记录: |