物事がどのように機能するかを説明しようとする2つのリンクを次に示します。
(1) (2)
それでは、できる限り手短に説明しようと思います。Windows フォーム アプリケーション内で発生することのほとんどは、単一のスレッドで発生します。通常、同じスレッドで Main() が実行されます。Program.cs を開くと、Main() に次のような行があることがわかります。
Application.Run(new Form1());
いつでもアプリケーションをデバッグしてコール スタックを調べると、その Run メソッドまでトレース バックすることがわかります。これは、Windows フォーム アプリケーションが実際には Run メソッドの継続的な実行であることを意味します。それで、ランは何をしているのですか?Run は、Windows がメッセージを送信するメッセージ キューを消費しています。Run は、それらのメッセージを正しいコントロールにディスパッチします。コントロールは、押されたキーに対応するテキストを追加したり、自分自身を再描画したりするなどのことを行います。これはすべて、単一のスレッドと一緒に無限ループが実行されている間に発生することに注意してください。または単にウィンドウを移動すると、それらのメッセージの負荷がアプリケーションに渡され、アプリケーションはそれらを処理し、それに応じて反応します。すべてその単一のスレッドで行われます。コントロールは、キューを介して自分自身にメッセージを送信することもできます。また、Control.BeginInvoke を介してポンプにメッセージを配置することもできます。これらのコントロールが行うことの 1 つは、何が起こるかに応じてイベントを発生させることです。したがって、ボタンをクリックすると、そのクリックを処理するために記述したコードが、Application.Run メソッドによって最終的かつ間接的に実行されます。
ここで、コードで何が起こっているかというと、プログレス バーの表示ステータスを表示に変更してからその値を更新しているにもかかわらず、すべて同じメソッドでその表示を false に変更しているということです。これは、メソッドを終了した後にのみ、Application.Run() がメッセージ キューの繰り返しと消費を継続できることを意味し、プログレス バーにその表示を更新するよう効果的に要求します。その場合、メソッドを終了する前に行った最後の処理である、進行状況バーの可視性を既に false のままにしています。DoEvents() は、キュー内のメッセージを読み取って処理するため、問題に対する迅速で汚い回避策です。再入可能性の問題が発生する可能性があるため、使用するのはあまり快適ではありません。
スレッドを使用することは良い解決策ですが、この種の状況ではカスタム スレッドの代わりに ThreadPool スレッドを使用することをお勧めします。それらのライフサイクルを制御します。スレッドを使用する最も簡単で実用的な方法は、BackgroundWorker コンポーネントを使用することですが、何が起こっているのかを本当に理解したい場合は、デリゲートを使用して Windows フォームのマルチスレッドを実行する方法を理解するのに苦労することをお勧めします。