【问题标题】:C# Is it thread safe to subscribe Same event handler for all ObjectsC#为所有对象订阅相同的事件处理程序是线程安全的吗
【发布时间】:2014-03-20 13:28:15
【问题描述】:

我的项目中有一种情况,我连接到多个服务器并监听事件。每当从服务器接收到一个事件时,Handler 应该将该事件添加到公共队列中进行处理。所有连接都应将接收到的事件添加到队列中。

foreach(var item in collection)
{
        Connection c = new connection(item);

         c.start();
         c.EventReceived+=new EventHandler(myHandler);
         list.add(c);
}

protected void  myHandler(eventArgs)
{
     //add it to the concurrent queue
}

我怀疑它是否能够在没有任何线程问题的情况下处理这些事件。如果您有任何模式或内置 API 可以安全地处理此问题,请告诉我。

【问题讨论】:

  • 你搞错了。它是不是线程安全的“列表”。订阅事件不是问题,因为对象是新的。需要锁定以防止在向列表添加对象时访问列表的任何代码。这里要小心,线程会吃掉你。

标签: c# .net multithreading event-handling thread-safety


【解决方案1】:

线程安全总是需要一个上下文——一个来自什么

如果你的意思是 +=

对于+=,这取决于事件的实现方式。如果它被实现为类似字段的事件,即

public event SomeEventType EventReceived;

然后是的:它是线程安全的。规范要求类字段事件的访问器是线程安全的,尽管实现可能会有所不同(例如,MS 编译器过去使用lock(this)lock(typeof(DeclaringType)),但现在它使用Interlocked)。

如果事件是手动实现的,即

public event SomeEventType EventReceived {
    add { ... }
    remove { ... }
}

那么线程安全完全由add/remove 实现定义。

如果你的意思是调用

那么这是线程安全的,因为委托是不可变的,但请注意,这一切都发生在调用线程的单个线程上。但是,一个常见的错误是在 null 测试中引入竞争条件:

if(EventReceived != null) EventReceived(this, someArgs);

以上内容不是线程安全的,因为从技术上讲,EventReceived 的值可以在测试后更改。为确保这不会出错,应该是:

var handler = EventReceived;
if(handler != null) handler(this, someArgs);

如果您指的是处理程序

那么线程安全完全由各个处理程序定义。例如,在 UI 应用程序中,处理程序必须检查并切换到 UI 线程。

【讨论】:

  • 感谢您的回答!!我将事件声明为字段,因此它是线程安全的。我认为处理程序调用将是同步的。有什么办法可以同时调用处理程序吗?
  • @Jajesh 如果你的意思是并发:当然 - 使用 GetInvocationList 扩展为单个代表,并在工作人员上单独调用它们
【解决方案2】:

引用自 C# 规范

When compiling a field-like event, the compiler automatically creates 
storage to hold the delegate, and creates accessors for the event that 
add or remove event handlers to the delegate field. In order to be thread-safe, 
the addition or removal operations are done while holding the lock (§8.12)
on the containing object for an instance event, or the type object (§7.6.10.6)
for a static event.

如果EventReceived 是类似事件的字段(非自定义实现),那么它的线程安全!

更新 当前实现使用 Interlocked 来保证线程安全

【讨论】:

  • 实际上,这不是当前的规范——它不再使用lock(事实上,ECMA 规范一直明确表示任何线程安全的实现都是有效的)跨度>
  • 我相信规范已经更新,更新的规范使用 Interlocked,正如刚才从反射器验证的那样。感谢指正!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多