Estoy tratando de crear una aplicación de consola asincrónica que funcione un poco en una colección. Tengo una versión que usa paralelo para bucle, otra versión que usa async / await. Esperaba que la versión async / await funcionara de manera similar a la versión paralela, pero se ejecuta sincrónicamente. ¿Qué estoy haciendo mal?
class Program
{
static void Main(string[] args)
{
var worker = new Worker();
worker.ParallelInit();
var t = worker.Init();
t.Wait();
Console.ReadKey();
}
}
public class Worker
{
public async Task<bool> Init()
{
var series = Enumerable.Range(1, 5).ToList();
foreach (var i in series)
{
Console.WriteLine("Starting Process {0}", i);
var result = await DoWorkAsync(i);
if (result)
{
Console.WriteLine("Ending Process {0}", i);
}
}
return true;
}
public async Task<bool> DoWorkAsync(int i)
{
Console.WriteLine("working..{0}", i);
await Task.Delay(1000);
return true;
}
public bool ParallelInit()
{
var series = Enumerable.Range(1, 5).ToList();
Parallel.ForEach(series, i =>
{
Console.WriteLine("Starting Process {0}", i);
DoWorkAsync(i);
Console.WriteLine("Ending Process {0}", i);
});
return true;
}
}
c#
.net
async-await
Satish
fuente
fuente
Ending Process
notificación no es cuando la tarea realmente está finalizando. Todas esas notificaciones se descargan secuencialmente justo después de que finaliza la última de las tareas. Para cuando vea "Finalizar proceso 1", es posible que el proceso 1 haya terminado durante mucho tiempo. Aparte de la elección de palabras allí, +1.Semaphore
inDoWorkAsync
para limitar el máximo de tareas de ejecución?Su código espera
await
a que finalice cada operación (uso ) antes de comenzar la siguiente iteración.Por tanto, no se obtiene ningún paralelismo.
Si desea ejecutar una operación asincrónica existente en paralelo, no es necesario
await
; solo necesita obtener una colección deTask
sy llamarTask.WhenAll()
para devolver una tarea que espera a todos ellos:return Task.WhenAll(list.Select(DoWorkAsync));
fuente
await
hace exactamente lo contrario de lo que desea: espera aTask
que termine.DoWorkAsync
en cada elementolist
(pasando cada elementoDoWorkAsync
, que supongo que tiene un solo parámetro)?public async Task<bool> Init() { var series = Enumerable.Range(1, 5); Task.WhenAll(series.Select(i => DoWorkAsync(i))); return true; }
fuente
En C # 7.0 puede usar nombres semánticos para cada uno de los miembros de la tupla , aquí está la respuesta de Tim S. usando la nueva sintaxis:
public async Task<bool> Init() { var series = Enumerable.Range(1, 5).ToList(); var tasks = new List<Task<(int Index, bool IsDone)>>(); foreach (var i in series) { Console.WriteLine("Starting Process {0}", i); tasks.Add(DoWorkAsync(i)); } foreach (var task in await Task.WhenAll(tasks)) { if (task.IsDone) { Console.WriteLine("Ending Process {0}", task.Index); } } return true; } public async Task<(int Index, bool IsDone)> DoWorkAsync(int i) { Console.WriteLine("working..{0}", i); await Task.Delay(1000); return (i, true); }
También puedes deshacerte del
task.
interiorforeach
:// ... foreach (var (IsDone, Index) in await Task.WhenAll(tasks)) { if (IsDone) { Console.WriteLine("Ending Process {0}", Index); } } // ...
fuente