2012-04-06 13 views
48

comienzo a unas pocas tareas en paralelo, como esto:¿Existe un Task.WaitAll genérico?

var tasks = 
    Enumerable.Range(1, 500) 
    .Select(i => Task.Factory.StartNew<int>(ProduceSomeMagicIntValue)) 
    .ToArray(); 

y luego unirlas con

Task.WaitAll(tasks); 

En esta última línea consigo un marcador azul ondulada bajo tasks, con un mensaje de advertencia:

Co-variant array conversion from Task[] to Task[] 
can cause run-time exception on write operation.

Entiendo por qué recibo este mensaje, pero ¿hay alguna forma de evitarlo? (Por ejemplo, como una versión genérica de Task.WaitAll()?)

+1

En este caso, la conversión es segura, porque 'WaitAll()' no escribirá en la matriz. ¿Hay alguna razón por la que quieres evitarlo? – svick

+11

Además, .Net 4.5 contendrá ['Task.WhenAll()'] (http://msdn.microsoft.com/en-us/library/system.threading.tasks.task.whenall%28v=vs.110% 29.aspx) que devuelve una sola 'Tarea' que se completa cuando se completan todas las' Tareas' en la colección. Y también tiene una versión genérica y funciona en cualquier 'IEnumerable ' de 'Task's. – svick

+0

@svick thx para la propina. parece que cambiaron el nombre de lo que estás diciendo cuando todo, así que puedes decir 'esperar tarea'.WhenAll (task1, task2); ' –

Respuesta

8

Usted puede crear un método de extensión para hacer esto.

no sé la aplicación exacta de WaitAll, pero podemos asumir que espera a cada artículo para completar:

static class TaskExtensions 
{ 
    public static void WaitAll<T>(this Task<T>[] tasks) 
    { 
     foreach (var item in tasks) 
     { 
      item.Wait(); 
     } 
    } 
} 

A continuación, llamar, a partir del código actual:

tasks.WaitAll(); 

Editar

La implementación real es un poco más compleja. He omitido el código de esta respuesta porque es bastante largo.

http://pastebin.com/u30PmrdS

Puede modificar esto para apoyar tareas genéricas.

+0

La "implementación real" es bastante no trivial. Sin mencionar, se pierde (o necesita volver a codificar) si la implementación de .NET Framework tiene alguna corrección de errores o mejoras en el rendimiento. Las soluciones de @MerickOWA y DMac the Destroyer (en ese orden) resuelven el problema del OP de manera muy simple. – Mike

+0

Esto obviamente es incorrecto: está ejecutando sus tareas * en serie * aquí, que no es lo que hace el verdadero 'WaitAll'. –

+0

@MarkAmery esto supone que las tareas ya se despachan cuando se introducen en este método. – Bas

21

Un método genérico de Task.WaitAll implicaría que todas las tareas tendrían que devolver el mismo tipo, que sería una utilidad extremadamente limitada. Escribir algo así podría hacerse manualmente (ver la respuesta de Bas Brekelmans), pero esto no permitirá ContinueWith o cancelación sin mucho trabajo.

Una solución simple si usted no está utilizando la matriz para todo lo demás es

.ToArray<Task>(); 
+0

+1 es una posibilidad, pero en mi caso, realmente utilizo la matriz para obtener los resultados 'int':' var results = tasks.Select (task => task.Result) ' – GolfWolf

+1

@ W0lf y simplemente hago un extra. ToArray () antes de pasarlo a Task.WaitAll, cualquier posible asignación a esta Tarea copiada [] no puede causar una excepción de tiempo de ejecución – MerickOWA

20

Estoy bastante seguro de que es una operación segura incluso con la advertencia, pero si realmente quería conseguir alrededor de él una mejor opción que la creación de su propia aplicación sería sólo para convertir el parámetro tasks en el tipo que quiere:

Task.WaitAll(tasks.Cast<Task>().ToArray()) 

que mata a los garabatos azules para mí, me permite mantener mi variable de tasks genérico y no me obliga a crear un montón de nuevo código miedo de que en última instancia es innecesaria.

+1

Esto funcionó perfectamente para mí. Lo probé tanto con las tareas que se ejecutaron hasta su finalización, como con las tareas que agotaron el tiempo, usando 'var ifAllTasksRanToCompletion = Task.WaitAll (tasks.Cast () .ToArray(), timeout: TimeSpan.FromSeconds (20));'. – Contango

Cuestiones relacionadas