如何在循环中使用await

Sat*_*ish 75 .net c# async-await

我正在尝试创建一个异步控制台应用程序,它可以对集合进行一些操作.我有一个版本使用并行for循环另一个版本使用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;
    }
}
Run Code Online (Sandbox Code Playgroud)

Tim*_* S. 104

你使用await关键字的方式告诉C#你想要在每次通过循环时等待,这是不平行的.你可以像这样重写你的方法来做你想要的,通过存储一个Tasks 的列表,然后将await它们全部用来Task.WhenAll.

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);
}
Run Code Online (Sandbox Code Playgroud)

  • 需要注意的是,当您看到"结束进程"通知时,**当任务实际结束时不是**.所有这些通知都会在任务完成*last*后立即逐出.当您看到"结束流程1"时,流程1可能已经过了很长时间.除了那里的单词选择,+ 1. (7认同)
  • 我不了解其他人,但对于并行循环来说,并行/ foreach似乎更直接. (3认同)

SLa*_*aks 34

您的代码await在开始下一次迭代之前等待每个操作(使用)完成.
因此,您没有任何并行性.

如果要并行运行现有的异步操作,则不需要await; 你只需要得到一个Tasks 的集合并调用它Task.WhenAll()来返回一个等待所有这些的任务:

return Task.WhenAll(list.Select(DoWorkAsync));
Run Code Online (Sandbox Code Playgroud)

  • @Satish:你可以.但是,`await`与你想要的完全相反 - 它为`Task`完成_waits_. (4认同)

Vla*_*mir 10

public async Task<bool> Init()
{
    var series = Enumerable.Range(1, 5);
    Task.WhenAll(series.Select(i => DoWorkAsync(i)));
    return true;
}
Run Code Online (Sandbox Code Playgroud)


Meh*_*ani 5

在 C# 7.0 中,您可以对 tuple 的每个成员使用语义名称,这是Tim S.使用新语法的答案:

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);
}
Run Code Online (Sandbox Code Playgroud)

你也可以摆脱task. insideforeach

// ...
foreach (var (IsDone, Index) in await Task.WhenAll(tasks))
{
    if (IsDone)
    {
        Console.WriteLine("Ending Process {0}", Index);
    }
}
// ...
Run Code Online (Sandbox Code Playgroud)