2012-10-03 16 views
5

Tenemos una función de servicio web que ejecuta una lista de tareas en segundo plano. Cuando se completan todas las tareas, vuelve. He volvió a examinar la función para C# 5. Me las he arreglado para obtener el siguiente fragmento de trabajo:Reescribir la espera de múltiples hilos usando async/await

var result = new List<int>(); 
var tasks = new List<Task<int>>(); 

foreach (var row in rowlist) 
    tasks.Add(Task.Factory.StartNew(() => DoWork(row))); 
foreach (var task in tasks) 
    result.Add(task.Result); 

Los task.Result espera la tarea individual por hacer. He probado esto agregando un retraso de tres segundos al método DoWork y verificando que termine en menos de 5 segundos, para obtener una lista de 10 tareas.

Ahora he intentado reescribir la función utilizando la nueva sintaxis async/await. Pero parece que no puedo hacer que funcione. Si se compila, se ejecuta de forma síncrona, como se verificó al agregar una espera.

Cualquier ideas sobre cómo se puede volver a escribir el código anterior utilizando async/await?

Respuesta

9

que iba a escribir esto como:

var tasks = rowlist.Select(row => Task.Run(() => DoWork(row))); 

var results = (await Task.WhenAll(tasks)).ToList(); 

Básicamente, todavía crean las tareas de la forma en que fueron previamente (sin utilizar await), a medida que los quiere a todos comenzar de inmediato. A continuación, puede esperar a que se completen de forma asíncrona (a través del await Task.WhenAll), y luego extraer los resultados de una vez (ya que sabe que todo está hecho).

+0

Esto se ejecuta de manera asincrónica. Pero da como resultado una excepción interna 'System.AggregateException'' {"Los resultados de la consulta no se pueden enumerar más de una vez."} '¿Alguna idea? – Andomar

+0

Quizás más importante: ¿por qué se ejecuta de forma sincronizada si omite la línea con 'await'? – Andomar

+1

@Andomar Llamada 'ToList' después de' Seleccionar' al crear las tareas. Eso debería arreglar el error. Actualmente, cuando se enumeran 'tasks' varias veces, se trataría de crear las nuevas tareas dos veces, lo que sería malo. – Servy

2

Sobre la base de Reed Copsey, Servy y Stephen de respuesta y comentarios Cleary he progresado a esta solución:

var results = rowlist 
    .Select(row => Task.Run(() => DoWork(row))) 
    .ToList() 
    .Select(t => t.Result); 

El segundo Select esperará en las tareas para terminar. He agregado un ToList() para evitar la transmisión, pero parece funcionar sin eso.

+1

Esto es esencialmente lo mismo que el código con el que comenzó. Necesita el 'await' allí ... – Servy

+0

@Servy: Ahora estoy confundido: ¿no fue usted quien comentó anteriormente que' task.Result' también puede bloquear? El código funciona por cierto: 10 tareas en 4 segundos, donde cada tarea tiene una suspensión de 3 segundos. – Andomar

+2

El objetivo de utilizar async/await es que todo el método (en el que se encuentra este código) no se bloqueará.Podría llamar a los métodos desde, por ejemplo, un evento en una aplicación de interfaz gráfica de usuario y no hacer que bloqueen el subproceso de la interfaz de usuario (mientras todavía se ejecuta todo el código necesario en el subproceso de interfaz de usuario). Usando 'await' el método realmente no devuelve los resultados, devuelve una tarea que le informará cuándo se calcularon esos resultados (y posiblemente cuáles fueron esos resultados). – Servy

Cuestiones relacionadas