分别等待 IEnumerable<Task<T>> C#

Awaiting IEnumerable<Task<T>> individually C#

简而言之,我有一个可枚举的任务,我想以 await 的方式 运行 数组中的每个任务。每个任务都将执行缓慢的网络操作,从我的角度来看,我只需要在任务完成后更新 WinForm UI。

下面是我目前正在使用的代码,但我认为这更像是一种 hack,而不是实际的解决方案:

private void btnCheckCredentials_Click(object sender, EventArgs e)
{
    // GetNetCredentials() is irrelevant to the question...
    List<NetworkCredential> netCredentials = GetNetCredentials();

    // This call is not awaited. Displays warning!
    netCredentials.ForEach(nc => AwaitTask(ValidateCredentials(nc)));
}

public async Task<bool> ValidateCredentials(NetworkCredential netCredential)
{
    // Network-reliant, slow code here...
}

public async Task AwaitTask(Task<bool> task)
{
    await task;

    // Dumbed-down version of displaying the task results
    txtResults.Text += task.Result.ToString();
}

显示 btnCheckCredentials_Click() 警告的第二行:

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

这实际上按照我想要的方式工作,因为我不想等待操作完成。相反,我只想取消任务,然后在每个任务完成后立即做一些事情。

Task.WhenAny() 或 Task.WhenAll() 方法的功能与我预期的一样,因为我想知道每项任务是否完成 - 只要它完成。 Task.WaitAll() 或 Task.WaitAny() 是阻塞的,因此也是不可取的。

编辑:所有任务应同时开始。然后他们可以按任何顺序完成。

您可以通过使用 Task.WhenAny 将其标记为 async 来执行此操作(async void 这里很好,因为您在事件处理程序中):

private async void btnCheckCredentials_Click(object sender, EventArgs e)
{
    // GetNetCredentials() is irrelevant to the question...
    List<NetworkCredential> netCredentials = GetNetCredentials();
    var credentialTasks = netCredentials
                          .Select(cred => ValidateCredentialsAsync(cred))
                          .ToList();

    while (credentialTasks.Count > 0)
    {
        var finishedTask = await Task.WhenAny(credentialTasks);

        // Do stuff with finished task.

        credentialTasks.Remove(finishedTask);
    }
}

您可以触发并忽略每个任务,并在任务完成时添加回调。

private async void btnCheckCredentials_Click(object sender, EventArgs e)
{
List<NetworkCredential> netCredentials = GetNetCredentials();

    foreach (var credential in netCredentials)
    {
        ValidateCredentails(credential).ContinueWith(x=> ...) {

        };
     }
}

因此,您可以创建回调方法并确切知道特定任务何时完成,而不是 labda 表达式。

您在寻找 Task.WhenAll 吗?

await Task.WhenAll(netCredentials.Select(nc => AwaitTask(ValidateCredentials(nc)));

您可以在AwaitTask.

中完成您需要的所有完成处理

await task;有点尴尬。我会这样做:

public async Task AwaitTask(netCredential credential)
{
    var result = await ValidateCredentails(credential);

    // Dumbed-down version of displaying the task results
    txtResults.Text += result.ToString();
}