如何将WaitAll()转换为async和await

本文关键字:async await 转换 WaitAll | 更新日期: 2023-09-27 18:13:00

是否可以使用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似乎没有等待所有任务完成。我如何让它这样做?返回类型表示它返回一个任务,但似乎无法弄清楚语法。##标题##

多线程新手,请原谅无知

如何将WaitAll()转换为async和await

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

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

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

private async Task PullTablePagesAsync()

await重写的主体(你已经正确地做了)。为了向后兼容,使用如下的新实现提供旧的同步版本

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

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

你期待错了。

await语法不应该阻塞——它只是执行流不应该继续,直到任务完成。

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

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