2011-08-04 15 views
5

私のWPFアプリケーションでは、2つの長時間実行されるタスクを並列に実行する必要があり、どちらもUIに表示する必要があるデータを返します。C#.NETタスク:複数のタスクが完了したときに通知を受け取る方法

私のビューモデルにIsBusyというプロパティがあります。これは両方のタスクが完了するまで真となるはずです。

2つの長時間実行されたタスクが完了したときに通知を受け取るにはどうすればよいですか?

私はUIスレッドをブロックするので、Task.WaitAllは使いたくありません。 ContinueWithを使用してタスクを連鎖させたくないのです。なぜなら、これらの長い実行中のタスクを並行して実行したいからです。

答えて

4

TaskScheduler.FromCurrentSynchronizationContext()を使用して、TaskFactory.ContinueWhenAll(...)に渡して、両方のタスクが完了した後にUIを更新します。

+0

これは私が探しているものです。私はフレームワークにこのようなものがあると思っていましたが、私はそれを見つけることができませんでした。 – Mas

0

あなたの答えはコールバックだと思います。

長期実行スレッドを設定する場合は、メソッドへの参照を渡します。コールバックメソッドをタスクメソッドに渡すか、デリゲートのBeginInvoke()機能に組み込まれているAsyncCallbackデリゲートインスタンスを設定することができます。あなたのコールバックメソッドがたIAsyncResultを受け入れ、特定のデリゲートの定義に準拠する必要があり、

public void StartMyTwoTasks() 
{ 
    //I'm passing the callback to the task method directly; you may prefer to pass it to BeginInvoke() 
    var task1Lambda =()=>Task1(TaskCompleted); 
    var task2Lambda =()=>Task2(TaskCompleted); 

    task1Lambda.BeginInvoke(null,null); 
    task2Lambda.BeginInvoke(null,null); 
} 

public void Task1(Action<int> task1Complete) 
{ 
    //perform your long-running calculation or data retrieval/transformation here 
    Thread.Sleep(10000); 
    //when finished, call the callback method. 
    //You may need to use Control.Invoke() to make sure the callback is executed on the UI thread 
    //If you use the AsyncCallback feature of BeginInvoke/EndInvoke, you don't have to make the call here. 
    taskComplete(1); 
} 

public void Task2(Action<int> taskComplete) 
{ 
    //Ditto 
    Thread.Sleep(8000); 

    taskComplete(2); 
} 

public void Task1Complete(int taskNumber) 
{ 
    TasksComplete[taskNumber-1] = true; 
    If(TasksComplete.All(x=>x==true)) 
     DoSomethingOnceAllTasksAreComplete(); 
} 

AsyncCallback機能を使用していますが、中に適切にそれを呼び出すことを心配する必要はありません。ここで

は、基本的な例ですあなたの方法;フレームワークはあなたのコールバックを処理します:

public public void StartALongTask() 
{ 
    var taskLambda =()=>PerformTask(); 

    taskLambda.BeginInvoke(TaskComplete,null); 
} 

//one advantage is that you can call a method that returns a value very easily. 
public IEnumerable<string> PerformTask() 
{ 
    //long-running task 
    Thread.Sleep(5000); 

    return Enumerable.Repeat("result", 100); 

    //look ma, no callback invocation. 
    //This makes asynchronous delegates very useful for refactoring a synchronous 
    //operation without a lot of code changes. 
} 

//Here's the callback, which is invoked properly by the runtime when the thread is complete 
public void TaskComplete(IASyncResult ar) 
{ 
    //calling EndInvoke on the same delegate, which is available in the AsyncResult, 
    //returns the return value of that delegate as if you'd called it synchronously. 
    var results = ar.AsyncDelegate.EndInvoke(ar); 

    //Now you can do something with those results. 
} 

これらのモデルはいずれもうまく機能します。他のオプションには、イベントの設定が含まれます。イベントは、完了すると各メソッドによって発生します。イベントは実際にはいくつかのシンタックスシュガーを持つ "マルチキャスト"デリゲートなので、ほぼ同じように動作します。

0

各タスクに対応するブール型フラグと、それらのフラグを監視する別のウォッチャスレッドを持つのはどうですか?処理から戻ったときに、それぞれのタスクが対応するフラグを設定するようにします。次に、監視スレッドは、フラグがセットされているかどうかを監視する(すなわち、両方のタスクが終了している)。プロセスが終了すると、イベントを発生させ、IsBusyをfalseに設定する。

4

これらのタスクを生成する3番目のスレッドであり、2つのタスクが完了するまでTask.WaitAllを使用してスレッドの終了を遅延させます。そのスレッドの最後でイベントを発生させるか、コールバック関数を実行することができます。

RunWorkerCompletedという組み込みのイベントを持つBackgroundWorkerを使用すると、さらに簡単にすることができます。これはあなたをスレッドプールに置くので、スレッドを管理することに関心を持つ必要はありません。

3

Try Task.Factory.ContinueWhenAllTasksの配列をとり、すべてが完了したら非同期で処理を開始します。

関連する問題