1

多くのスレッドが GUI を更新するために呼び出すことができる次のコードがあります。

MethodInvoker del = () => { lblInfo.Text = tmp; };
lblInfo.BeginInvoke(del);

(lblInfo は GUI スレッドによって作成されます)

また、GUI スレッドによって実行されるボタン クリックでこのメソッドが呼び出されます。

public void Stop()
{
    isStopping = true;
    crawler.Join();
    foreach (Thread t in txtWorkers)
    {
        t.Join();
    }
    indexer.Join();     
    lblStatus.Text = "Stopped";
    lblInfo.Text = "";
}

停止ボタンのクリックで1回100回以上プログラムがデッドロックします。デッドロックを見たときはデバッグしていなかったので、さまざまなスレッドの状態についてはわかりませんが、参加しているすべてのスレッドが最終的に isStopping値をチェックして終了するポイントに到達することはほぼ確実です。これにより、に問題がある可能性があると思いますが、BeginInvoke実際には見つけることができません。それを呼び出すスレッド (クローラーとインデクサー) がブロックされないように、非同期にする必要があります。Stop()GUI スレッドが実行中で、 からの呼び出しも実行する必要がある場合はどうなりBeginInvokeますか? これが問題でしょうか?参加しているスレッドについて表示されないものはありますか?

編集: 提案された変更後の​​コードの外観:

public void Stop()
{
    /*
     ...disable GUI
     */

    isStopping = true; // Declared as volatile
    lblStatus.Text = "Stopping...";

    // Creating a thread that will wait for other threads to terminate
    Task.Factory.StartNew(() =>
    {    
        crawler.Join();
        foreach (Thread t in txtWorkers)
        {
            t.Join();
        }
        indexer.Join();

        // Adjust UI now that all threads are terminated
        MethodInvoker del = () =>
        {
            /*
            ...enable GUI
            */
            lblStatus.Text = "Not Running";
            isStopping = false;
        };
        lblStatus.BeginInvoke(del);
    });
}

機能しているようです。デッドロックが解消されることを願っています...

4

1 に答える 1

4

バックグラウンドスレッドは、GUIが追いつくのを待たずにその行を過ぎて進むだけなので、それは問題ではないと思います。どこでも使用している場合、デッドロックが発生する可能性があります。BeginInvokeInvokeControl.Invoke

さらに重要なことJoinに、GUI スレッドでの使用は基本的に悪い考えです。UI はすべてが完了するまで凍結されます。新しいものを開始できるコントロールを無効にし、isStoppingフラグを設定してから、すべてのスレッドが停止するのを待つ新しいスレッドを作成し、すべてのスレッドが終了したら、 UI を再度更新することをお勧めしBeginInvokeます。(.NET 4.5 を使用している場合は、非同期メソッドを使用して、すべてのスレッドを待機するタスクを作成して待機することもできます。)

最後に、isStoppingが単なるboolフィールドの場合、バックグラウンド スレッドが UI スレッドからの変更を「見る」という保証はありません。フィールドを揮発性にすることでこれが修正される可能性がありますが、揮発性の正確な意味は私を怖がらせます。別の方法は、クラスを使用するInterlockedか、読み取りと書き込みの両方でロックを取得するプロパティにすることです。これにより、適切なメモリバリアが確実に配置されます。

于 2013-09-03T08:20:48.403 に答える