J'essaie de créer une application console asynchrone qui travaille sur une collection. J'ai une version qui utilise la boucle parallèle pour une autre version qui utilise async / await. Je m'attendais à ce que la version async / await fonctionne de manière similaire à la version parallèle, mais elle s'exécute de manière synchrone. Qu'est-ce que je fais 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
la source
la source
Ending Process
notification, ce n'est pas lorsque la tâche se termine réellement. Toutes ces notifications sont vidées de manière séquentielle juste après la fin de la dernière tâche. Au moment où vous voyez "Fin du processus 1", le processus 1 est peut-être terminé depuis longtemps. Autre que le choix des mots, +1.Semaphore
inDoWorkAsync
pour limiter le maximum de tâches d'exécution?Votre code attend la fin de chaque opération (utilisation
await
) avant de commencer l'itération suivante.Par conséquent, vous n'obtenez aucun parallélisme.
Si vous souhaitez exécuter une opération asynchrone existante en parallèle, vous n'avez pas besoin
await
; il vous suffit d'obtenir une collection deTask
s et d'appelerTask.WhenAll()
pour renvoyer une tâche qui les attend tous:return Task.WhenAll(list.Select(DoWorkAsync));
la source
await
fait exactement le contraire de ce que vous voulez - il attend laTask
fin.DoWorkAsync
sur chaque élémentlist
(en passant chaque élément dansDoWorkAsync
, qui, je suppose, a un seul paramètre)?public async Task<bool> Init() { var series = Enumerable.Range(1, 5); Task.WhenAll(series.Select(i => DoWorkAsync(i))); return true; }
la source
En C # 7.0, vous pouvez utiliser des noms sémantiques pour chacun des membres du tuple , voici la réponse de Tim S. en utilisant la nouvelle syntaxe:
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); }
Vous pouvez également vous débarrasser de l'
task.
intérieurforeach
:// ... foreach (var (IsDone, Index) in await Task.WhenAll(tasks)) { if (IsDone) { Console.WriteLine("Ending Process {0}", Index); } } // ...
la source