割り込みのようなものですか...
いいえ、まったくありません。コードの実行でビジー状態のときにスレッドを中断する安全な方法はありません。これは、「再入可能性バグ」と呼ばれる特に厄介なタイプの問題を引き起こします。これは、ファームウェアプログラマーが組み込みシステムに割り込みハンドラーを実装するときに戦う一種のバグです。このWebページの背景。
プログラムのUIスレッドは、これを別の方法で解決します。これは、生産者/消費者問題の標準ソリューションで消費者の役割を果たします。成分は、プロデューサーがメッセージを投稿するスレッドセーフキューであり、コンシューマースレッドのディスパッチャーループです。キューからメッセージを取得し、メッセージに関連付けられたメッセージハンドラーを実行します。これは、「メッセージループのポンピング」とよく言われます。プロデューサーは最も一般的なオペレーティングシステムであり、ユーザーがキーを押したり、マウスを動かしたりするなどのメッセージを生成します。ただし、別のスレッドを含め、メッセージを生成する任意のコードにすることができます。
Winformsは、このスキームに追加のキュー、invokeキューを追加します。これには、コードが作成したデリゲートオブジェクトと、指定した引数が格納されます。Begin / Invokeは、呼び出しキューにエントリを追加し、PostMessage()をピンボークして、UIスレッドに何かを行う必要があることを通知します。
UIスレッドがコードの実行でビジー状態の場合、たとえばペイントイベントの処理である場合、これは気づかれません。ディスパッチャループに再び入り、GetMessage()を呼び出すと、再びアイドル状態になるまで、投稿されたメッセージに気付くことはありません。または、すでにアイドル状態になっている場合は、メッセージに非常に迅速に応答します。呼び出しキュー内のエントリを取得し、デリゲートターゲットを実行します。
BeginInvokeではなくInvokeの場合、キューエントリでManualResetEventのSet()メソッドを呼び出します。スレッドが待機しているスレッドは、実行を再開します。デリゲートメソッドが失敗した場合、この時点で発生した例外もスレッドで再発生します。
それが機能する方法から引き出すことができるいくつかの基本的な結論:
- スレッドが完全に実行を再開する速度は、UIスレッドのビジー状態によって異なります。
- 一般に、BeginInvokeを使用すると、ワーカースレッドがブロックされなくなります。
- 開始/呼び出し呼び出しは自動的にシリアル化され、先入れ先出しで、追加のロックは必要ありません
- ただし、BeginInvokeを使用する場合、デリゲートは後で実行されるまで実行されないため、デリゲートターゲットメソッドに提供するデータが実行時までに有効であることを確認する必要があります。ロックが必要な場合があります
- BeginInvokeの代わりにInvokeを使用すると、デッドロックが発生する可能性があります。これは、UIスレッドがアイドル状態ではないが、何か他のことが起こるのを待っているときに発生します。スレッドが終了するのを他の何かが待っているときにデッドロックが保証されます。InvokeよりBeginInvokeを好むもう1つの理由
- UIスレッドがデリゲートターゲットを実行できるよりも速く呼び出すと、問題が発生します。UIスレッドは追いつくことができず、呼び出しキューは際限なく大きくなります。これに気づきやすいので、UIスレッドは優先度の低いタスクの処理を停止します。ペイントを含むと、UIはフリーズしたように見えます
- スレッドが破棄されたフォームまたはコントロールを呼び出そうとすると、大きな問題が発生します。これは通常、ユーザーがウィンドウを閉じ、ワーカースレッドの実行が停止したことを確認しない場合に発生します。これは通常クラッシュを引き起こし、ObjectDisposedExceptionが最も一般的な結果です