线程安全事件调用 [英] Thread Safe Event Calls

查看:82
本文介绍了线程安全事件调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在触发事件时避免竞争条件(在多线程应用中)的一种常见做法是:

A common practice to avoid race conditions (in multi-threaded apps) when triggering events is this:

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."

问题(根据本书)是编译器可以优化此代码以完全删除本地temp变量.如果发生这种情况,则此版本的代码与第一个版本相同,因此NullReferenceException为仍然有可能"

The problem (according to the book) 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"

根据通过C#进行的CLR,这是强制编译器复制事件指针的更好方法.

According to CLR via C#, here is a better way to force the compiler to copy the event pointer.

virtual void OnNewMail(NewMailEventArgs e)
{
    EventHandler<NewMailEventArgs> temp =
                          Interlocked.CompareExchange(ref NewMail, null, null);
    if (temp != null) 
        temp(this, e);
}

在这里,CompareExchange将NewMail引用更改为null(如果为null),并且不更改NewMail(如果不为null).换句话说,CompareExchange根本不会更改NewMail中的值,但会以原子,线程安全的方式返回NewMail中的值. 里弗(Richter),杰弗里(Jeffrey)(2010-02-12).通过C#进行CLR(第265页). OReilly Media-A. Kindle版.

Here, CompareExchange changes the NewMail reference to null if it is null and does not alter NewMail if it is not null. In other words, CompareExchange doesn't change the value in NewMail at all, but it does return the value inside NewMail in an atomic, thread-safe way. Richter, Jeffrey (2010-02-12). CLR via C# (p. 265). OReilly Media - A. Kindle Edition.

我使用的是.Net 4.0框架,不确定该如何工作,因为Interlocked.CompareExchange期望引用位置,而不是事件.

I am on .Net 4.0 framework, and not sure how this can possibly work, because Interlocked.CompareExchange expects a reference to a location, not a reference to a event.

这本书有误,或者我误解了.有没有人实现这种方法?还是在这里有更好的方法来防止比赛条件?

Either there is an error in the book, or I misinterpreted it. Has anyone implemented this method? Or have a better way to prevent race conditions here?

更新

这是我的错误,迭代代码有效.我只是指定了错误的转换,但是根据Bradley(如下)所述,在.net 2.0及更高版本的Windows中没有必要.

it was my mistake, the iterlocked code works. i just had wrong casting specified, but according to Bradley (below) it is not necessary in .net 2.0 and up on windows.

推荐答案

不允许编译器(或JIT)优化该if/temp(在CLR 2.0及更高版本中); CLR 2.0内存模型不允许从堆读取数据到被介绍(规则2).

The compiler (or JIT) is not allowed to optimize that if/temp away (in CLR 2.0 and later); the CLR 2.0 Memory Model does not allow reads from the heap to be introduced (rule #2).

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

Thus, MyEvent can not be read a second time; the value of temp must be read in the if statement.

有关此情况的详细讨论,请参见我的博客帖子,并解释为什么标准模式很好.

See my blog post for an extended discussion of this situation, and an explanation of why the standard pattern is fine.

但是,如果您在不提供CLR 2.0内存模型保证(但仅遵循ECMA内存模型)的非Microsoft CLR(例如,单声道)上运行,或者在Itanium(其上运行具有众所周知的较弱的硬件内存模型),您将需要诸如Richter's之类的代码来消除潜在的竞争条件.

However, if you are running on a non-Microsoft CLR (e.g., mono) that doesn't provide the CLR 2.0 memory model guarantees (but only follows the ECMA memory model), or you're running on Itanium (which has a notoriously weak hardware memory model), you will need code like Richter's to eliminate a potential race condition.

关于您关于Interlocked.CompareExchange的问题,语法public event EventHandler<NewMailEventArgs> NewMail只是C#语法糖,用于声明类型为EventHandler<NewMailEventArgs>的私有字段以及具有addremove方法的公共事件. Interlocked.CompareExchange调用读取私有EventHandler<NewMailEventArgs>字段的值,因此此代码确实可以按照Richter的描述进行编译和工作.在Microsoft CLR中完全没有必要.

In regards to your question about Interlocked.CompareExchange, the syntax public event EventHandler<NewMailEventArgs> NewMail is just C# syntactic sugar for declaring a private field of type EventHandler<NewMailEventArgs> and a public event that has add and remove methods. The Interlocked.CompareExchange call reads the value of the private EventHandler<NewMailEventArgs> field, so this code does compile and work as Richter describes; it's just unnecessary in the Microsoft CLR.

这篇关于线程安全事件调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆