2

我有一个问题,如何在多线程环境中为异步回调正确添加/删除事件处理程序。

我有 MyCore 类,它接收来自 ProxyDLL 的异步回调,它从非托管代码调度回调。我有订阅事件的表单(托管)。

附加/分离事件的正确方法是什么。我注意到 MulticastDelegate 有 _invocationcount。它能做什么?如果回调调用正在进行直到回调完成,事件的内部逻辑是否会阻止与事件分离?该 puprose 是否存在 _invocationcount?脱离事件(通常)是安全的吗?

class Form1
{
  EventHandler m_OnResponse;
  Int32 m_SomeValue;
  Form1()
  {
    m_OnResponse = new EventHandler(OnResponseImpl);
    m_MyCore.SetCallBackOnLogOn(m_OnResponse);
  }
  ~Form1()
  {
    m_MyCore.ReleaseCallBackOnLogOn(m_OnResponse);
  }
  private OnResponseImpl(object sender, EventArgs e)
  {
    Thread.Sleep(60*1000);

    m_SomeValue = 1;             // <<-- How to/Who guarantees that Form1 obj is still
                                 // alive. May be callback was invoked earlier and
                                 // we just slept too long

    if (!this.IsDisposed)
    {
        invokeOnFormThread(DoOnResponseImpl, sender, e);
    }
  }
}

class MyCore
{
  private event EventHandler OnLogOn;
  public void SetCallBackOnLogOn(EventHandler fn)
  {
    // lock (OnLogOn)
    {
        OnLogOn += fn;
    }
  }
  ReleaseCallBackOnLogOn(EventHandler fn)
  {
    // lock (OnLogOn)
    {
        OnLogOn -= fn;
    }
  }
  public void DoDispatchOnLogOn()
  {
    // lock (OnLogOn)
    {
      if (OnLogOn != null)
      {
        OnLogOn(this, null);
      }
    }
  }
}
4

1 回答 1

3

默认事件添加和删除操作已经是线程安全的。在大多数情况下,您不需要担心那部分。您需要担心的是多播委托的调用。

public void DoDispatchOnLogOn()
{
  EventHander local;
  lock (this)
  {
    local = OnLogOn;
  }
  if (local != null)
  {
    local(this, null);
  }
}

我在这里所做的是创建一个保存OnLogOn委托链的局部变量。我在这里利用多播委托的不变性,以便我们可以对 null 和调用序列进行线程安全检查。lock仅用于确保“新鲜”阅读,如果OnLogon您不介意“陈旧”阅读,则它是严格可选的。

更新:

我需要确保在 ReleaseCallBackOnLogOn 结束取消订阅委托时不会调用回调。

在大多数情况下,我的代码不会尝试执行任何已取消订阅的事件处理程序。在删除事件处理程序和引发事件之间存在轻微的竞争,这可能导致事件处理程序被执行,即使它最近才被删除。

我需要确保我的类实例仍然存在,直到对回调的调用完全完成。

委托持有对包含目标方法的类实例的引用。这将使您的实例保持扎根,因此不符合垃圾收集的条件。您无需担心这一点。

我应该指出,~Form1终结器不是删除事件处理程序的好地方。请记住,委托持有对包含目标方法的实例的引用,因此在大多数情况下不会调用终结器并且不会从事件中删除事件处理程序。

于 2012-04-17T18:17:04.110 回答