【问题标题】:How to convert WaitAll() to async and await如何将 WaitAll() 转换为异步并等待
【发布时间】:2015-12-13 13:57:35
【问题描述】:

是否可以使用 async await 语法重写下面的代码?

private void PullTablePages()
{
    var taskList = new List<Task>();
    var faultedList = new List<Task>();

    foreach (var featureItem in featuresWithDataName)
    {
        var task = Task.Run(() => PullTablePage();
        taskList.Add(task);

        if(taskList.Count == Constants.THREADS)
        {
            var index = Task.WaitAny(taskList.ToArray());
            taskList.Remove(taskList[index]);
        }
    }

    if (taskList.Any())
    {
        Task.WaitAll(taskList.ToArray());
    }

    //todo: do something with faulted list
}

当我如下重写它时,代码不会阻塞,并且控制台应用程序在大多数线程完成之前完成。

看起来 await 语法没有像我预期的那样阻塞。

private async void PullTablePagesAsync()
{
    var taskList = new List<Task>();
    var faultedList = new List<Task>();

    foreach (var featureItem in featuresWithDataName)
    {
        var task = Task.Run(() => PullTablePage();

        taskList.Add(task);

        if(taskList.Count == Constants.THREADS)
        {
            var anyFinished = await Task.WhenAny(taskList.ToArray());

            await anyFinished;

            for (var index = taskList.Count - 1; index >= 0; index--)
            {
               if (taskList[index].IsCompleted)
               {
                  taskList.Remove(taskList[index]);
               }
            }        
        }
    }

    if (taskList.Any())
    {
       await Task.WhenAll(taskList);
    }

    //todo: what to do with faulted list?
}

有可能吗?

WaitAll 似乎并未等待所有任务完成。我如何让它这样做?返回类型表示它返回一个任务,但似乎无法弄清楚语法。## Heading ##

多线程新手,请见谅。

【问题讨论】:

  • PullTablePagesAsync 的返回类型更改为Task 并在您的主函数/循环中等待它。
  • 你到底想在这里做什么?您的代码目前没有多大意义。
  • 运行任务列表。如果一个完成,则开始另一个,直到一次完成 4 个限制
  • Async/await 不用于重写阻塞代码。事实上,它们是专门设计为非阻塞的。这个问题没有意义。
  • async void 仅用于与事件兼容。如果PullTablePagesAsync 不是事件处理程序(它看起来不像),则不应使用async void。见“Async/Await - Best Practices in Asynchronous Programming

标签: c# multithreading asynchronous async-await c#-5.0


【解决方案1】:

是否可以使用 async await 语法重写下面的代码?

是和不是。是的,因为你已经做到了。不,因为虽然从函数 实现 的角度来看是等价的,但从函数 caller 的角度来看它是不等价的(正如您在控制台应用程序中已经体验过的那样)。与许多其他 C# 关键字相反,await 不是语法糖。编译器强制您使用 async 标记您的函数以启用 await 构造是有原因的,原因是现在您的函数不再阻塞,这给调用者带来了额外的责任 - 要么把自己非阻塞 (async) 或对您的函数使用阻塞调用。实际上,每个async 方法都应该返回TaskTask&lt;TResult&gt;,然后编译器会警告忽略该事实的调用者。唯一的例外是async void,它应该用于事件处理程序,它本质上不应该关心被通知的对象正在做什么。

简而言之,async/await 不是为了重写同步(阻塞代码),而是为了轻松将其转换为异步(非阻塞)。如果您的函数应该是同步的,那么就保持原样(并且您的原始实现完美地做到了这一点)。但是如果你需要异步版本,那么你应该把签名改为

private async Task PullTablePagesAsync()

使用 await 重写的正文(您已经正确地做到了)。为了向后兼容,使用这样的新实现提供旧的同步版本

private void PullTablePages() { PullTablePagesAsync().Wait(); }

【讨论】:

  • 似乎我误以为我可以轻松阻止/等待我的问题中的异步等待构造
【解决方案2】:

看起来 await 语法没有像我预期的那样阻塞。

你期待错了。

await 语法不应阻塞 - 只是在任务完成之前执行流程不应继续。

通常您在返回任务的方法中使用 async/await。在您的情况下,您在返回类型为 void 的方法中使用它。 了解异步 void 方法需要花费一些时间,这就是为什么通常不鼓励使用它们的原因。 Async void 方法与调用方法同步(阻塞)运行,直到等待第一个(未完成)任务。之后会发生什么取决于执行上下文(通常您在池上运行)。重要的是:调用方法(调用PullTablePAgesAsync 的方法)不知道延续,也不知道PullTablePagesAsync 中的所有代码何时完成。

也许可以看看 MSDN 上的 async/await 最佳实践

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-05-17
    • 1970-01-01
    • 2016-01-18
    • 1970-01-01
    • 1970-01-01
    • 2021-01-22
    • 1970-01-01
    相关资源
    最近更新 更多