他の場所で発生したイベントを「リッスン」するフォームがあります(フォーム自体やその子コントロールの1つではありません)。イベントは、フォームが破棄された後も存在するオブジェクトによって発生し、フォーム ハンドルが作成されたスレッド以外のスレッドで発生する可能性があります。つまり、イベント ハンドラーで Invoke を実行する必要があります (フォームなど)。
(オーバーライドされた) フォームのDispose(bool)
メソッドで、このメソッドが呼び出されたときにまだサブスクライブされている可能性のあるすべてのイベントからサブスクライブを解除しました。ただし、Invoke は、イベント ハンドラーの 1 つから呼び出されることがあります。これは、イベントがサブスクライブ解除される直前にイベント ハンドラーが呼び出され、OS が制御を実行する dispose メソッドに切り替えてから、破棄されたオブジェクトで Invoke メソッドを呼び出すハンドラーに制御を戻すためだと思います。
メインスレッドが呼び出されたメソッドを処理するまで、Invoke を呼び出すと呼び出しスレッドがロックされるため、スレッドをロックしても役に立ちません。メイン スレッド自体が、Invoke 呼び出しスレッドが取得したオブジェクトのロックの解放を待機している可能性があり、デッドロックが発生する可能性があるため、これは発生しない可能性があります。
要するに、別のスレッドで発生する可能性のある外部イベントにサブスクライブされているときに、フォームを正しく破棄するにはどうすればよいですか?
現時点でいくつかの主要な方法がどのように見えるかを次に示します。このアプローチは、上で説明した問題に苦しんでいますが、それらを修正する方法がわかりません。
これは、モデルのデータ部分の変更を処理するイベント ハンドラーです。
private void updateData()
{
if (model != null && model.Data != null)
{
model.Data.SomeDataChanged -= new MyEventHandler(updateSomeData);
model.Data.SomeDataChanged += new MyEventHandler(updateSomeData);
}
updateSomeData();
}
これは、ビューに変更を加える必要があるイベント ハンドラーです。
private void updateSomeData()
{
if (this.InvokeRequired) this.myInvoke(new MethodInvoker(updateSomeData));
else
{
// do the necessary changes
}
}
そして myInvoke メソッド:
private object myInvoke(Delegate method)
{
object res = null;
lock (lockObject)
{
if (!this.IsDisposed) res = this.Invoke(method);
}
return res;
}
メソッドの私のオーバーライドDispose(bool)
:
protected override void Dispose(bool disposing)
{
lock (lockObject)
{
if (disposing)
{
if (model != null)
{
if (model.Data != null)
{
model.Data.SomeDataChanged -= new MyEventHandler(updateSomeData);
}
// unsubscribe other events, omitted for brevity
}
if (components != null)
{
components.Dispose();
}
}
base.Dispose(disposing);
}
}
更新 (アランの要求による):
Dispose メソッドを明示的に呼び出すことはありません。フレームワークによって行われます。これまでのところ、デッドロックはアプリケーションが閉じられたときにのみ発生しました。ロックを行う前に、フォームが単純に閉じられたときにいくつかの例外がスローされることがありました。