2012-07-25 10 views
15
var tasks = new List<Task>(); 

foreach (var guid in guids) 
{ 
    var task = new Task(...); 
    tasks.Add(task); 
} 

foreach (var task in tasks) 
{ 
    task.Start(); 
    Task.WaitAll(task); 
} 

Se ejecuta el subproceso de la interfaz de usuario. Necesito ejecutar todas las tareas en tareas variables una después de la otra. El problema es si llamo Task.WaitAll (task), la IU se congela. ¿Cómo puedo hacer la siguiente lógica sin tener la IU congelada?Encadenamiento de tareas (espere a que se complete la tarea anterior)

+4

Entonces, ¿por qué no haces un gran delegado? – SLaks

+0

para agregar a lo que dijo @SLaks, si solo tiene una lista de cosas para ejecutar, usar algo como BackgroundWorker podría ser más simple. Lo realmente desconocido es lo que desea que suceda cuando el trabajo en segundo plano esté terminado. Suponiendo que se trate de algún tipo de actualización de UI, BackgroundWorker ya lo maneja ejecutando el delegado completo en el hilo de la interfaz de usuario. –

Respuesta

22

Esto no es tarea de encadenamiento.

Debe realizar el encadenamiento de tareas usando ContinueWith. La última tarea necesitaría actualizar la UI.

Task.Factory.StartNew(() => DoThis()) 
    .ContinueWith((t1) => DoThat()) 
    .ContinueWith((t2) => UpdateUi(), 
     TaskScheduler.FromCurrentSynchronizationContext()); 

Nota la última línea tiene TaskScheduler.FromCurrentSynchronizationContext() esta tarea se asegurará se ejecutará en el contexto de sincronización (IU Tema).

4

Es necesario utilizar continutations:

lastTask.ContinueWith(() => newTask.Start()); 
14

La mejor manera es utilizar la Biblioteca paralelo de tareas (TPL) y continuaciones. Una continuación no solo le permite crear un flujo de tareas sino que también maneja sus excepciones. Este es un great introduction para el TPL. Pero para darle una idea ...

Puede iniciar una tarea TPL usando

Task task = Task.Factory.StartNew(() => 
{ 
    // Do some work here... 
}); 

ahora para iniciar una segunda tarea cuando un antecedente finalice la tarea (por error o éxito) se puede utilizar el método ContinueWith

Task task1 = Task.Factory.StartNew(() => Console.WriteLine("Antecedant Task")); 
Task task2 = task1.ContinueWith(antTask => Console.WriteLine("Continuation...")); 

Así que tan pronto como se complete task1, falla o se cancela task2 'incendios-up' y comienza a ejecutarse. Tenga en cuenta que si se hubiera completado task1 antes de llegar a la segunda línea del código task2, se programará para ejecutarse inmediatamente. El argumento antTask pasado a la segunda lambda es una referencia a la tarea antecedente. Ver this link para ejemplos más detallados ...

También puede pasar continuaciones resultados de la tarea antecedente

Task.Factory.StartNew<int>(() => 1) 
    .ContinueWith(antTask => antTask.Result * 4) 
    .ContinueWith(antTask => antTask.Result * 4) 
    .ContinueWith(antTask =>Console.WriteLine(antTask.Result * 4)); // Prints 64. 

Nota. Asegúrese de leer sobre el manejo de excepciones en el primer enlace provisto, ya que esto puede llevar a un recién llegado al TPL por mal camino.

Una última cosa a tener en cuenta en particular para lo que quiere es tareas para niños. Las tareas secundarias son aquellas que se crean como AttachedToParent. En este caso, la continuación no se ejecutará hasta que todas las tareas secundarias han completado

TaskCreationOptions atp = TaskCreationOptions.AttachedToParent; 
Task.Factory.StartNew(() => 
{ 
    Task.Factory.StartNew(() => { SomeMethod() }, atp); 
    Task.Factory.StartNew(() => { SomeOtherMethod() }, atp); 
}).ContinueWith(cont => { Console.WriteLine("Finished!") }); 

espero que esto ayude.

Cuestiones relacionadas