Задачи C # .NET: Как получить уведомление о завершении нескольких задач - PullRequest
5 голосов
/ 04 августа 2011

В моем приложении WPF мне нужно параллельно запускать 2 длительные задачи, обе из которых возвращают данные, которые необходимо отобразить в пользовательском интерфейсе.

В моей модели представления есть свойство IsBusy,должно быть истинным до тех пор, пока обе задачи не будут завершены.

Как получить уведомление о завершении 2 длительных задач?

Я не хочу использовать Task.WaitAll, потому что он заблокирует мой поток пользовательского интерфейса.Я не хочу связывать задачи с помощью ContinueWith, потому что я хочу, чтобы эти долго выполняющиеся задачи выполнялись параллельно.

Ответы [ 5 ]

5 голосов
/ 04 августа 2011

Используйте TaskScheduler.FromCurrentSynchronizationContext () и передайте его TaskFactory.ContinueWhenAll (...) для обновления пользовательского интерфейса после выполнения обеих задач.

4 голосов
/ 04 августа 2011

Попробуйте Task.Factory.ContinueWhenAll. Он принимает массив Tasks и асинхронно запускает операцию, когда все они завершены.

4 голосов
/ 04 августа 2011

Один из способов - создать третий поток, который порождает эти задачи и использует Task.WaitAll для задержки выхода из потока до завершения двух задач. В конце этого потока вы можете запустить событие или выполнить функцию обратного вызова.

Вы можете сделать это еще проще, используя BackgroundWorker , который имеет встроенное событие под названием RunWorkerCompleted . Это приведет к тому, что вы попадете в пул потоков, так что вам не нужно будет заботиться об управлении этим потоком.

0 голосов
/ 04 августа 2011

Как насчет наличия логических флагов, соответствующих каждой задаче, и другого отдельного потока наблюдателя, который отслеживает эти флаги? Каждой задаче установите соответствующие флаги, когда они вернутся из обработки. Затем поток мониторинга отслеживает, установлены ли флаги (т. Е. Обе задачи завершены). Если процессы завершены, запустите событие, установите для IsBusy значение false и т. Д.

0 голосов
/ 04 августа 2011

Я думаю, что ваш ответ - обратный вызов.

При настройке ваших давно работающих потоков передайте ссылку на метод.Вы можете передать метод обратного вызова в метод задачи или настроить экземпляр делегата AsyncCallback, который встроен в функцию делегатов BeginInvoke ().

Вот базовый пример:

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, ваш метод обратного вызова должен соответствовать определению определенного делегата, которое принимает IAsyncResult, но вам не нужно беспокоиться о его правильном вызове в вашем методе;фреймворк обрабатывает обратный вызов для вас:

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.
}

Обе эти модели должны нормально работать для вас.Другие параметры включают настройку события, которое затем вызывается каждым методом после его завершения.Это будет работать почти так же, поскольку события на самом деле являются просто «многоадресными» делегатами с некоторым синтаксическим сахаром.

...