2

非同期コールバックのマルチスレッド環境でイベント ハンドラーを適切に追加/削除する方法について質問があります。

アンマネージ コードからコールバックをディスパッチする ProxyDLL から非同期コールバックを受け取る MyCore クラスがあります。イベントをサブスクライブするフォーム(マネージド)があります。

イベントへのアタッチ/デタッチへの正しいアプローチは何でしょうか。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 に答える