Estou tentando criar um aplicativo de console assíncrono que faz algum trabalho em uma coleção. Eu tenho uma versão que usa paralelo para loop outra versão que usa async / await. Eu esperava que a versão assíncrona / aguardar funcionasse de forma semelhante à versão paralela, mas executa de forma síncrona. O que estou fazendo errado?
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
fonte
fonte
Ending Process
notificação não é quando a tarefa está realmente terminando. Todas essas notificações são despejadas sequencialmente logo após o término da última tarefa. Quando você vir "Finalizando Processo 1", o processo 1 pode ter acabado há muito tempo. Além da escolha de palavras lá, +1.Semaphore
inDoWorkAsync
para limitar o máximo de tarefas de execução?Seu código espera que cada operação (usando
await
) termine antes de iniciar a próxima iteração.Portanto, você não obtém nenhum paralelismo.
Se você deseja executar uma operação assíncrona existente em paralelo, não é necessário
await
; você só precisa obter uma coleção de seTask
chamarTask.WhenAll()
para retornar uma tarefa que espera por todos eles:return Task.WhenAll(list.Select(DoWorkAsync));
fonte
await
faz exatamente o oposto do que você deseja - espera que oTask
termine.DoWorkAsync
em cada item emlist
(passando cada item paraDoWorkAsync
, que suponho que tenha um único parâmetro)?public async Task<bool> Init() { var series = Enumerable.Range(1, 5); Task.WhenAll(series.Select(i => DoWorkAsync(i))); return true; }
fonte
Em C # 7.0 você pode usar nomes semânticos para cada um dos membros da tupla . Aqui está a resposta de Tim S. usando a nova sintaxe:
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); }
Você também pode se livrar de
task.
dentroforeach
:// ... foreach (var (IsDone, Index) in await Task.WhenAll(tasks)) { if (IsDone) { Console.WriteLine("Ending Process {0}", Index); } } // ...
fonte