C#:スレッドセーフイベント
-
10-07-2019 - |
質問
以下の実装はスレッドセーフですか?そうでない場合、私は何が欠けていますか?どこかにvolatile
キーワードが必要ですか?または、OnProcessingCompleted
メソッドのどこかにロックしますか?もしそうなら、どこですか?
public abstract class ProcessBase : IProcess
{
private readonly object completedEventLock = new object();
private event EventHandler<ProcessCompletedEventArgs> ProcessCompleted;
event EventHandler<ProcessCompletedEventArgs> IProcess.ProcessCompleted
{
add
{
lock (completedEventLock)
ProcessCompleted += value;
}
remove
{
lock (completedEventLock)
ProcessCompleted -= value;
}
}
protected void OnProcessingCompleted(ProcessCompletedEventArgs e)
{
EventHandler<ProcessCompletedEventArgs> handler = ProcessCompleted;
if (handler != null)
handler(this, e);
}
}
注: プライベートイベントと明示的なインターフェイススタッフがある理由は、抽象基本クラスだからです。そして、それを継承するクラスは、そのイベントを直接処理するべきではありません。より明確になるようにクラスラッパーを追加しました=)
解決
プライベートなProcessCompleted
メンバーがevent
である必要はありません-それは単にフィールドである可能性があります:private EventHandler<ProcessCompletedEventArgs> ProcessCompleted;
-クラス内では常にフィールドに直接移動するため、public event EventHandler<ProcessCompletedEventArgs> ProcessCompleted;
スタッフはとにかく負けました。
明示的なロックオブジェクトを使用して示したアプローチは、フィールドのようなイベントを持つよりもスレッドセーフではありません(つまりthis
-唯一の違いは、 '<!> quot; this <!> quot;をロックしない(これは良いことです-<=>でのロックは避けるべきです理想的に).. <!> quot; handler変数<! > quot;アプローチは正しい方法ですが、まだ注意すべき副作用。
他のヒント
ハンドラーをフェッチするときにもロックする必要があります。そうしないと、最新の値を取得できない可能性があります。
protected void OnProcessingCompleted(ProcessCompletedEventArgs e)
{
EventHandler<ProcessCompletedEventArgs> handler;
lock (completedEventLock)
{
handler = ProcessCompleted;
}
if (handler != null)
handler(this, e);
}
これは、一連のハンドラーを実行し、1つのハンドラーをサブスクライブ解除することを決定した競合状態を 防止しません。 handler
変数に含まれるマルチキャストデリゲートを取得したため、これは引き続き呼び出されます。
ハンドラー自体に、これ以上呼び出されるべきではないことを認識させる以外に、これについてできることはあまりありません。
イベントをスレッドセーフにするためには、トライしないことをお勧めします-イベントを発生させるスレッド内でサブスクリプションがのみ変更されるように指定します。