C# 安全地引发事件线程

C# 安全地引发事件线程,c#,.net,multithreading,events,C#,.net,Multithreading,Events,我肯定我以前见过这种情况,但我想知道我应该如何安全地发起一个事件线程 我有一个消息发送线程,看起来很像 while(_messages > 0){ Message msg; // get next message if (MessageDispatched != null) MessageDispatched(this, new MessageDispatchedEventArgs(msg.Msg, msg.Params)); } 我可以看出,

我肯定我以前见过这种情况,但我想知道我应该如何安全地发起一个事件线程

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

while(_messages > 0){

    Message msg;
    // get next message

    if (MessageDispatched != null)
        MessageDispatched(this, new MessageDispatchedEventArgs(msg.Msg, msg.Params));
}
我可以看出,
MessageDispatched
在检查后可能为空。我在微软的博客上看到:

var handler = MessageDispatched;

if (handler != null)
    handler(this, new MessageDispatchedEventArgs(msg.Msg, msg.Params));
这确实阻止了检查发生后引用变为空的可能性。我想知道如何处理委托被处置的情况(或者即使可以?)

我是否需要在它周围坚持尝试/捕捉,因为它可能很少发生

编辑

在阅读了答案后,我考虑让我的班级来处理这个问题——很快,它看起来像下面的内容,但我遇到了一些问题,使它不像我想要的那样干净——也许有人知道如何做到这一点

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);
            }
        }
    }
}
公共类ThreadSafeEvent
//何处TDelegate:代表为什么这是不允许的特殊情况???
{
列表_委托=新列表();
公开作废订阅(TDelegate@delegate)
{
锁定(_代表)
{
如果(!\u delegate.Contains(@delegate))
_delegates.Add(@delegate);
}
}
公共无效取消订阅(TDelegate@delegate)
{
锁定(_代表)
{
_删除(@delegate);
}
}
//如何从代表处获得签名?
公共无效提升(参数对象[]\u参数)
{
锁定(_代表)
{
foreach(TDelegate wrappedDel in_代表)
{
var del=作为代表的wrappedDel;
del.Method.Invoke(del.Target,_参数);
}
}
}
}

请检查以下内容:

后一种结构将确保在非安腾体系结构上调用处理程序时不会出现空引用异常

但是,这会导致另一个可能的问题——注册了事件处理程序的客户机可能会在删除该处理程序后调用该处理程序。防止这种情况发生的唯一方法是序列化引发事件并注册处理程序。但是,如果这样做,可能会出现死锁情况


简而言之,有三种可能的方法可以打破这种局面——我同意您在这里所做的方式(MS建议),并接受事件处理程序在注销后被调用的可能性。

阅读Eric Lippert的这篇文章:

您通过编辑添加的代码可能存在死锁。考虑这个:对象订阅处理程序。在线程A上,获取一个锁。在线程B上,发生了需要引发事件的事件。在处理程序中,它尝试获取线程A持有的锁,阻塞。然后线程A尝试注册事件的处理程序,导致死锁(每个线程持有一个锁,另一个线程正在等待)。IMHO,这比其他任何一个问题都要糟糕(不做任何事情导致的NullRef,或者MS推荐方式导致的幻影事件)。