by WebSurfer
2019年7月11日 16:43
Windows Forms のマルチスレッドアプリで不正なクロススレッドコールがある場合、Visual Studio から[デバッグの開始(S)]でなら以下のような例外が捕捉されるが、[デバッグなしで開始(H)]では捕捉されないという話を書きます。
元の話は Teratail の「非同期処理でデバッグ時・ビルド実行時で処理結果が異なる?」というスレッドから来ています。
例えば、Windows Forms アプリの Button.Click のイベントハンドラで、以下のように AsParallel 拡張メソッドを使ってマルチスレッドで処理するケースを考えます。
private void button4_Click(object sender, EventArgs e)
{
Enumerable.Range(1, 10).AsParallel().ForAll(z =>
{
this.textBox2.Text += z.ToString();
});
}
this.textBox2.Text += z.ToString(); の行で、textBox2 が作成された UI スレッドとは別のスレッドから textBox2 の呼び出しを行っています。これが不正なクロススレッドコールになります。
このコードを Visual Studio から[デバッグの開始(S)]で実行した場合は、デバッガがこの行で InvalidOperationException 例外をスローし、"コントロールが作成されたスレッド以外のスレッドからコントロール 'textBox2' がアクセスされました" というエラーメッセージを表示します。それが上の画像です。
ところが[デバッグなしで開始(H)]で実行した場合は例外はスローされず、あたかもこのコードは正常終了したかのように見えます。集約的例外ハンドラで捕捉してアプリケーションを終了させるということもできません(そもそも例外がスローされないので)。
という訳で、特にマルチスレッドアプリを開発している場合は必ず[デバッグの開始(S)]で実行して動作確認をするようにした方がよさそうです。
なお、[デバッグなしで開始(H)]でも CheckForIllegalCrossThreadCalls プロパティを true(デフォルトは false)に設定すれば不正なクロススレッドコールで例外をスローさせることはできます。
でも、たぶんオーバーヘッドが増えてパフォーマンスに影響があるでしょうからリリース前には元に戻さなければならず、そんな面倒なことをするより[デバッグの開始(S)]で実行して確認するのが正解だと思います。