web-dev-qa-db-ja.com

ループでawaitを使用する方法

コレクションで何らかの作業を行う非同期コンソールアプリを作成しようとしています。非同期forループを使用する1つのバージョンと、async/awaitを使用する別のバージョンがあります。 async/awaitバージョンはパラレルバージョンと同様に動作するはずですが、同期的に実行されます。私は何を間違えていますか?

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;
    }
}
74
Satish

awaitキーワードの使用方法は、ループを通過するたびに待機することをC#に伝えますが、これは並行ではありません。 Tasksのリストを保存してから Task.WhenAll ですべてをawaitingすることで、このようにメソッドを書き換えて、目的の処理を実行できます。

public async Task<bool> Init()
{
    var series = Enumerable.Range(1, 5).ToList();
    var tasks = new List<Task<Tuple<int, bool>>>();
    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.Item2)
        {
            Console.WriteLine("Ending Process {0}", task.Item1);
        }
    }
    return true;
}

public async Task<Tuple<int, bool>> DoWorkAsync(int i)
{
    Console.WriteLine("working..{0}", i);
    await Task.Delay(1000);
    return Tuple.Create(i, true);
}
101
Tim S.

コードは、各操作(awaitを使用)が完了するのを待ってから、次の反復を開始します。
したがって、並列性は得られません。

既存の非同期操作を並行して実行する場合、awaitは不要です。 Tasksのコレクションを取得し、Task.WhenAll()を呼び出して、それらすべてを待機するタスクを返す必要があります。

return Task.WhenAll(list.Select(DoWorkAsync));
32
SLaks
public async Task<bool> Init()
{
    var series = Enumerable.Range(1, 5);
    Task.WhenAll(series.Select(i => DoWorkAsync(i)));
    return true;
}
9
Vladimir