2011-08-04 6 views
5

En mi aplicación WPF, necesito ejecutar 2 tareas de ejecución larga en paralelo, ambas devuelven datos que deben mostrarse en la IU.C# .NET Tareas: Cómo recibir notificaciones cuando se han completado varias tareas

Tengo una propiedad en mi modelo de vista llamada IsBusy, que debería ser cierta hasta que ambas tareas se hayan completado.

¿Cómo recibo una notificación cuando se completan las 2 tareas largas?

No quiero usar Task.WaitAll, porque bloqueará mi subproceso de interfaz de usuario. No quiero encadenar las tareas con ContinueWith, porque quiero que esas tareas de larga ejecución se ejecuten en paralelo.

Respuesta

4

Use TaskScheduler.FromCurrentSynchronizationContext() y páselo a TaskFactory.ContinueWhenAll (...) para realizar la actualización de la UI después de que se completen ambas tareas.

+0

Esto es exactamente lo que estoy buscando. Sospeché que había algo como esto en el marco, simplemente no pude encontrarlo. – Mas

0

Creo que su respuesta es una devolución de llamada.

Al configurar sus hilos de larga ejecución, pase una referencia a un método. Puede pasar el método de devolución de llamada al método de tarea, o puede configurar una instancia de delegado AsyncCallback, que está incorporada en la función BeginInvoke() de delegados.

Aquí hay un ejemplo básico:

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(); 
} 

Uso de la función AsyncCallback, el método de devolución de llamada tiene que ajustarse a una definición delegado particular que acepta el IAsyncResult, pero usted no tiene que preocuparse de que la invoca correctamente tu método; el marco maneja la devolución de llamada para usted:

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

Ambos modelos deberían funcionar bien para usted. Otras opciones incluyen la configuración de un evento, que luego es planteado por cada método cuando se completa. Funcionaría casi de la misma manera, ya que los eventos son en realidad delegados "multi-cast" con algo de sintaxis de azúcar.

0

¿Qué tal tener una bandera booleana correspondiente a cada tarea, y otra secuencia de observación separada que monitorea esas banderas? Haga que cada tarea establezca sus banderas correspondientes cuando regresen del procesamiento. Luego, el hilo de monitoreo observa si las banderas están establecidas (es decir, ambas tareas han finalizado). Si los procesos han finalizado, active un evento, establezca IsBusy en falso, etc.

4

Una forma sería crear un tercer hilo que engendra esas tareas, y usa Task.WaitAll para retrasar la salida del hilo hasta que se completen las dos tareas. Al final de ese hilo, puede disparar un evento o ejecutar una función de devolución de llamada.

Puede hacerlo aún más fácil usando BackgroundWorker, que tiene un evento incorporado llamado RunWorkerCompleted. Esto te pondría en el grupo de subprocesos para que no tengas que preocuparte tanto por administrar ese hilo.

3

Probar Task.Factory.ContinueWhenAll. Lleva una matriz de Tasks y desencadena de forma asincrónica una operación cuando están completas.

Cuestiones relacionadas