Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 线程安全事件调用_C#_Multithreading_.net 4.0 - Fatal编程技术网

C# 线程安全事件调用

C# 线程安全事件调用,c#,multithreading,.net-4.0,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

触发事件时避免竞态条件(在多线程应用程序中)的常见做法如下:

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."
EventHandler temp=SomeEvent;
如果(温度!=null)温度(e);
“请记住,委托是不可变的,这就是为什么这种技术在理论上是有效的。然而,许多开发人员没有意识到的是,编译器可以优化此代码以完全删除本地临时变量。如果发生这种情况,此版本的代码与第一个版本相同,因此仍然存在NullReferenceException。”不可能。”
问题是(根据这本书)编译器可以优化此代码以完全删除本地temp变量。如果发生这种情况,此版本的代码与第一个版本相同,因此仍然可能出现NullReferenceException

根据CLR via C#,这里有一个更好的方法来强制编译器复制事件指针

virtual void OnNewMail(NewMailEventArgs e)
{
    EventHandler<NewMailEventArgs> temp =
                          Interlocked.CompareExchange(ref NewMail, null, null);
    if (temp != null) 
        temp(this, e);
}
virtualvoid OnNewMail(NewMailEventArgs e)
{
事件处理程序温度=
Interlocated.CompareExchange(参考NewMail,null,null);
如果(温度!=null)
温度(本,e);
}
在这里,如果NewMail引用为null,则CompareExchange将其更改为null;如果NewMail引用不为null,则不进行更改。换句话说,CompareExchange根本不会更改NewMail中的值,但它会以原子、线程安全的方式返回NewMail中的值。 里克特,杰弗里(2010-02-12)。CLR通过C#(第265页)。OReilly媒体-A.Kindle版

我使用的是.Net 4.0 framework,不确定它如何工作,因为Interlocked.CompareExchange需要对位置的引用,而不是对事件的引用

要么书中有错误,要么我误解了。有人实施过这种方法吗?或者有更好的方法来防止这里的比赛情况

更新

这是我的错误,iterlocked代码可以工作。我只是指定了错误的强制转换,但根据Bradley(以下)的说法,在.NET2.0及以上的windows上,这是不必要的

编译器(或JIT)不允许优化
if/temp
away(在CLR 2.0及更高版本中);不允许引入堆中的读取(规则#2)

因此,
MyEvent
无法再次读取;必须在
if
语句中读取
temp
的值

有关这种情况的详细讨论,以及标准模式为何适用的解释,请参见

但是,如果您运行的是不提供CLR 2.0内存模型保证(但仅遵循ECMA内存模型)的非Microsoft CLR(如mono),或者您运行的是安腾(其硬件内存模型非常弱),那么您将需要类似Richter的代码来消除潜在的争用情况


关于你关于
interlocated.compareeexchange
的问题,语法
public event handler NewMail
只是声明类型为
EventHandler
的私有字段和具有
add
remove
方法的公共事件的C#语法糖。
Interlocked.compareeexchange
调用读取私有
EventHandler
字段的值,因此此代码确实按照里希特所描述的编译和工作;这在Microsoft CLR中是不必要的。

我想您可能错过了。位置是指指向对象引用的指针[msdn版本:与比较对象进行比较并可能被替换的目标对象。]。以下代码在.NET4.0中运行良好

public class publisher
{

    public event EventHandler<EventArgs> TestEvent;
    protected virtual void OnTestEvent(EventArgs e)
    {
        EventHandler<EventArgs> temp = Interlocked.CompareExchange(ref TestEvent, null, null);
        if (temp != null)
            temp(this,e);
    }
}
公共类发布程序
{
公共事件处理程序测试事件;
受保护的虚拟void OnTestEvent(事件参数e)
{
EventHandler temp=Interlocked.compareeexchange(ref TestEvent,null,null);
如果(温度!=null)
温度(本,e);
}
}

现在,这只是对您问题的部分回答,因为我无法对使用Interlocked.CompareeExchange发表评论,但我认为这些信息可能有用

问题是编译器可能会优化if/temp

好吧,根据CLR通过C#(第264-265页)

编译器可以优化代码以完全删除局部[…]变量。如果发生这种情况,此版本的代码与[引用事件两次的版本]相同,因此仍然可能出现NullReferenceException

因此,是有可能的,但是,重要的是要知道Microsoft的即时(JIT)编译器不会优化局部变量。虽然这可能会改变,但不太可能,因为它可能会破坏许多应用程序

这是因为.Net具有强大的内存模型:

不能引入读取和写入

然而,该模型不允许引入读取,因为这意味着从内存中重新读取一个值,并且在低锁代码内存中可能会发生变化

然而,遵循a的Mono可以优化局部变量

底线:除非你打算使用Mono,否则别担心


即使这样,这种行为也可以通过volatile声明来抑制。

如果您查看生成的IL,您将看到该方法是这样调用的

IL_000d:  ldsflda    class [mscorlib]System.EventHandler`1<class [mscorlib]System.EventArgs> ConsoleApplication1.Program::MyEvent
IL_0012:  ldnull
IL_0013:  ldnull
IL_0014:  call       !!0 [mscorlib]System.Threading.Interlocked::CompareExchange<class [mscorlib]System.EventHandler`1<class [mscorlib]System.EventArgs>>(!!0&,!!0,!!0)

可能很有趣我用作者的评论更新了我的示例。如果你所说的是真的,那么它与杰弗里·里克特所说的直接冲突。我以前在这本书中发现过他不准确的陈述。。我只想确认这样做的正确方法..仅在windows/.Net complier/JIT中,这对于Mono是不正确的@凯塞:说得好;我确实在我的博客文章中提到了这一点,但没有在回答中提到;我会更新。@SonicSoul:是的,我直接反驳了里希特。IIRC,CLR via C#是他声称调用
IDisposable.Dispose
是不必要的,我也完全不同意这一点。:-)@BradleyGrainger,你是说第一个例子是per
.field private static class [mscorlib]System.EventHandler`1<class [mscorlib]System.EventArgs> MyEvent