1

おそらく、私はこのトピックを正しく理解していません。ここに問題があります...

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()作業スレッドの先頭に追加すると、ダイアログが表示され、すべて正常に動作します。

そのような行動を説明できますか?

4

2 に答える 2

2

ここで行う最善の方法は、他のフォームが最初に表示されるまで、長時間実行されるタスクが実際に開始されないようにすることです。Shownのイベントのおかげで、これはそれほど難しいことではありませんForm

private void btnQuery_Click(object sender, EventArgs e)
{
    querier = new Querier();
    OutputForm outputForm = new OutputForm();
    outputForm.Shown += delegate { querier.ProcessAll(outputForm.OutputReceived); };
    outputForm.ShowDialog();
}
于 2012-11-06T16:02:28.040 に答える
0

すみません、答えを見つけました!

InvokeRequired()がを返す特殊なケースが 1 つあり"false"ます。コントロールのハンドルがまだ作成されていないときです。この場合、電話をかけることは禁止されています。Invoke()つまり、InvokeRequired()はあなたを保護しようとします。

CreateHandle()の CTOR でメソッドを呼び出しますOutputForm。この方法では、ダイアログが表示される前でもハンドルが作成されるためInvokeRequired()、期待どおりに機能します。

于 2012-11-06T16:02:44.837 に答える