おそらく、私はこのトピックを正しく理解していません。ここに問題があります...
C# Windows アプリケーション (.NET 2.0)。MainForm には「クエリ」ボタンがあります。ユーザーがプッシュすると、次のようになります。
private void btnQuery_Click(object sender, EventArgs e)
{
querier = new Querier();
OutputForm outputForm = new OutputForm();
querier.ProcessAll(outputForm.OutputReceived);
outputForm.ShowDialog();
}
Querier
労働者です。バックグラウンド スレッドを作成し、それを実行して処理を行います。これは、作業スレッドの出力を表示する複数行のテキスト ボックスをOutputForm
持つ単純なフォームです。txtOutput
作業スレッドがその出力を送信できるようにするために、querier.ProcessAll()
メソッドはコールバック ハンドラを受け取ります。これはその実装です:
public void OutputReceived(string message)
{
if (this.InvokeRequired)
this.Invoke((MethodInvoker)delegate() { this.OutputReceived(message); });
else if (!string.IsNullOrEmpty(message))
txtOutput.AppendText(message + Environment.NewLine);
}
OutputReceived()
基本的に、作業スレッドは を使用するメソッドを使用して出力を実行し、送信します。これはInvoke()
、作業スレッドが txtOutput フィールドに直接アクセスできないためです。
outputForm.ShowDialog()
AFTER と呼ばれることに注意してくださいquerier.ProcessAll()
。ShowDialog()
ブロックしているからです。
しかし、ここに問題があります。ダイアログが実際に表示される前に作業スレッドが出力を送信すると、クロススレッド操作に関する例外が発生します! デバッグすると、何らかの理由this.InvokeRequired()
でOutputReceived()
メソッドが"false"
!を返すことがわかります。そのため、作業スレッドがtxtOutput
直接アクセスしようとしてクラッシュします。
問題は明らかに、スレッドと の間の競合状態に関するものShowDialog()
です。Thread.Sleep()
作業スレッドの先頭に追加すると、ダイアログが表示され、すべて正常に動作します。
そのような行動を説明できますか?