5

Taskは開始していて、WPF アプリで完了するのを待ちたいと思っています。Actionこのタスク内で、ディスパッチャでを呼び出します。

私が使用Task.Wait()すると、メソッドが終了していないかのようにハングしているように見えます。また、Dispatcher.Invoke 内のブレークポイントがヒットすることはありません。

私が使用Task.RunSyncronously()すると、正しく動作しているように見え、Dispatcher 内のブレークポイントがヒットします。

なぜ違いがあるのですか?

以下のコードサンプル:

public void ExampleMethod()
{
    // When doing the following:
    var task = new Task(LoadStuff);

    // This never returns:
    task.Start();
    task.Wait();

    // This version, however, does:
    task.RunSyncronously();
}

private void LoadStuff()
{
    ObservableCollection<StuffObj> stuff = Stuff.Load(arg1, true);

    DispatchHelper.RunOnDispatcher(() =>
    {
        ...
    });
}

public static class DispatchHelper
{
    public static void RunOnDispatcher(Action action)
    {
        Application.Current.Dispatcher.Invoke(action);
    }
}    
4

1 に答える 1

5

はい、大きな違いがあります。使用する場合RunSyncronouslyは、UI スレッドでタスクを実行するだけです。バックグラウンド スレッドで起動するとWait、コードはバックグラウンド スレッドで実行され、UI スレッドはブロックされます。そのタスク内のコードが UI スレッドを呼び出しており、UI スレッドが (によってWait) ブロックされている場合、デッドロックが発生しており、アプリケーションはフリーズしたままになります。

RunSyncronously非 UI スレッドからそのタスクを使用し、UI スレッドが他の何かによってブロックされていた場合でも、デッドロックが発生することに注意してください。

さて、あなたがすべきことは、実際には2つのオプションがあります:

  1. タスク自体は実際には時間がかからず、バックグラウンド スレッドではなく UI スレッドで実行する必要があります。UI スレッドは、このすべての作業を UI で直接実行するのに問題が生じるほど長く (一時的に) 凍結されることはありません。このような場合は、おそらくそれをタスクにすることさえすべきではなく、コードをメソッドに入れてメソッドを呼び出すだけです。

  2. タスクの実行には時間がかかり、その作業を行った後に UI を更新します。その場合はRunSyncronously、バックグラウンド スレッドで開始するのではなく、開始することが重要です。アプリケーション全体のデッドロックを防ぐには、呼び出しによって UI スレッドをブロックしないWaitようにする必要があります。タスクの終了後に実行したいコードがある場合に必要なことは、タスクに継続を追加することです。C# 4.0 ではContinueWith、タスクを呼び出し、実行するデリゲートを追加することでこれを行うことができました。C# 5.0 以降では、代わりawaitに関連するタスクを実行できます (Waitこれは実際には大きな違いです)、メソッドの残りの部分を自動的に接続して、継続として実行します (実際には、明示的なContinueWith呼び出しの構文糖衣ですが、非常に便利です)。

于 2012-11-01T20:48:08.577 に答える