web-dev-qa-db-ja.com

C#デリゲートはスレッドセーフですか?

デリゲートメンバー変数を持つクラスインスタンスがあり、複数のスレッドがそのデリゲートを呼び出す場合(実行時間の長いメソッドを指していると想定)、競合の問題はありますか?

デリゲートをロックする必要がありますか、それとも各スレッドが独自の呼び出しスタックを取得するため、各スレッドがデリゲートが指すメソッドを呼び出すのは安全ですか?

24
manning18

デリゲートの呼び出しに関して、答えはイエスです。

デリゲートは不変であるため、デリゲートの呼び出しはスレッドセーフです。ただし、デリゲートが最初に存在することを確認する必要があります。このチェックでは、必要な安全性のレベルに応じて、いくつかの同期メカニズムが必要になる場合があります。

たとえば、nullチェックと呼び出しの間に別のスレッドによってNullReferenceExceptionがnullに設定された場合、以下はSomeDelegateをスローする可能性があります。

if (SomeDelegate != null)
{    
  SomeDelegate();
}

以下はもう少し安全です。ここでは、デリゲートが不変であるという事実を利用しています。別のスレッドがSomeDelegateを変更したとしても、その厄介なNullReferenceExceptionを防ぐためにコードは困難です。

Action local = SomeDelegate;
if (local != null)
{
  local();
}

ただし、別のスレッドでSomeDelegateにnull以外の値が割り当てられている場合、デリゲートが実行されない可能性があります。これは、微妙なメモリバリアの問題と関係があります。以下が最も安全な方法です。

Action local = Interlocked.CompareExchange(ref SomeDelegate, null, null);
if (local != null)
{
  local();  
}

デリゲートによって参照されるメソッドの実行に関して、答えはノーです。

同期メカニズムを使用して、独自のスレッドセーフ保証を提供する必要があります。これは、CLRがデリゲートの実行に対してスレッドセーフ保証を自動的に提供しないためです。特に共有状態にアクセスしない場合は、メソッドを安全にするためにそれ以上の同期を必要としない場合があります。ただし、メソッドが共有変数から読み取りまたは書き込みを行う場合は、複数のスレッドからの同時アクセスを防ぐ方法を検討する必要があります。

26
Brian Gideon

いいえ、それらはスレッドセーフではありません。はい、並行性を自分で管理する必要があります。

7
Bala R

MulticastDelegate のドキュメントから直接:

このタイプのパブリック静的(Visual Basicで共有)メンバーはすべてスレッドセーフです。インスタンスメンバーは、スレッドセーフであることが保証されていません。

Delegate クラスには同じ情報が含まれているので、そこにあります。

6
Ed S.

イベントの変更はスレッドセーフではありませんが、デリゲートの呼び出しはスレッドセーフです。デリゲートは不変であるため、スレッドセーフです。ここの備考を参照してください MSDNデリゲートクラス

借用元 ここ :CLR Via C#で、Richterは、マルチスレッドクラスでのイベント呼び出しに関するいくつかの微妙な点を指摘しています。

デリゲートチェーンは不変です。最初のチェーンを置き換えるために新しいチェーンが作成されます。サブスクライバーがゼロのデリゲートチェーンはnullです。つまり、(イベントが公開されている場合)いつでもnullから非nullに、またはその逆に移行する可能性があります。

2
agent-j