2009-11-25 92 views
2

我确定我以前见过这个,但我想知道应该如何提高事件线程安全。安全地提高事件线程

我有一个消息发送线程看起来很像。

while(_messages > 0){ 

    Message msg; 
    // get next message 

    if (MessageDispatched != null) 
     MessageDispatched(this, new MessageDispatchedEventArgs(msg.Msg, msg.Params)); 
} 

我可以看到,在检查后可能会出现MessageDispatched为空。从我见过的MS博客:

var handler = MessageDispatched; 

if (handler != null) 
    handler(this, new MessageDispatchedEventArgs(msg.Msg, msg.Params)); 

这确实会停止引用在检查发生后变为null的可能性。我想知道如何处理代理人处置的情况(或者甚至可以处理)

我应该只需要在它周围贴上一个try/catch,因为它可能很少会发生?

编辑

阅读的答案后,我考虑过让我的类来处理这个 - 很快它看起来什么下文,但我遇到了,这使得它不干净,因为我想的几个问题 - 也许有人有一个想法如何做到这一点?

public class ThreadSafeEvent<TDelegate> 
// where TDelegate : Delegate  why is this a non allowed special case??? 
{ 
    List<TDelegate> _delegates = new List<TDelegate>(); 

    public void Subscribe(TDelegate @delegate) 
    { 

     lock (_delegates) 
     { 
      if (!_delegates.Contains(@delegate)) 
       _delegates.Add(@delegate); 
     } 
    } 

    public void Unsubscibe(TDelegate @delegate) 
    { 
     lock (_delegates) 
     { 
      _delegates.Remove(@delegate); 
     } 
    } 


    // How to get signature from delegate? 
    public void Raise(params object[] _params) 
    { 
     lock (_delegates) 
     { 
      foreach (TDelegate wrappedDel in _delegates) 
      { 
       var del = wrappedDel as Delegate; 
       del.Method.Invoke (del.Target, _params); 
      } 
     } 
    } 
} 
+1

与您通过编辑添加的代码的问题是可能的死锁。考虑一下:一个对象订阅你的处理程序。在线程A上,获取一个锁。在线程B上,发生了一些事件,需要事件引发。在处理程序中,它试图获取线程A阻塞的锁。线程A然后尝试在事件上注册一个处理程序,导致死锁(每个线程持有另一个正在等待的锁)。恕我直言,这比任何其他问题(NullRef从无所事事,或从MS建议的方式幻影事件)更糟糕。 – 2009-11-30 01:35:44

回答

1

后者的结构将确保你不会得到一个空引用异常呼吁非安腾架构的处理器。

但是,它导致了另一个可能的问题 - 它可能为注册事件处理程序有处理程序调用后它被删除的客户端。防止这种情况的唯一方法是序列化引发事件并注册处理程序。但是,如果你这样做,你就有潜在的僵局。

总之,有三种潜在的方式可以打破 - 我按照你在这里做的方式(和MS建议)行事,并接受事件处理程序可能在它之后被调用未注册。