WhenAny 在某些情况下表现得像 WhenAll

Det*_*ras 1 c# task-parallel-library async-await .net-5

因此,我在使用第三方库时遇到了问题,即使调用cancellationToken.Cancel. 下面是一个原型,可以解决这种情况并且它可以工作。

public async Task MainAsync()
{
    try
    {
        await StartAsync().ConfigureAwait(false);
    }
    catch (Exception ex)
    {
        Console.WriteLine("Exception thrown");
    }
}

private async Task<string> StartAsync()
{
    var cts = new CancellationTokenSource();

    cts.CancelAfter(3 * 1000);

    var tcs = new TaskCompletionSource<string>();

    cts.Token.Register(() => { Console.WriteLine("Cancelled"); tcs.TrySetCanceled(); });

    return await (await Task.WhenAny(tcs.Task, LongComputationAsync())
        .ConfigureAwait(false)).ConfigureAwait(false);
}

private async Task<string> LongComputationAsync()
{
    await Task.Delay(1 * 60 * 1000).ConfigureAwait(false);

    return "Done";
}
Run Code Online (Sandbox Code Playgroud)

所以上面的代码会等待 3 秒,然后它会抛出一个TaskCancelledException应该的异常。如果您随后将方法更改LongComputationAsync为以下内容:

private Task<string> LongComputationAsync()
{
    Task.Delay(1 * 60 * 1000).ConfigureAwait(false).GetAwaiter().GetResult();

    return Task.FromResult("Done");
}
Run Code Online (Sandbox Code Playgroud)

我仍然希望它具有相同的行为,但它的作用是,它将等待整整 1 分钟(在 中指定LongComputationAsync()),然后抛出TaskCancelledException.

谁能向我解释一下吗?关于这是如何运作的,或者这是否是正确的行为。

Ste*_*ary 5

谁能向我解释一下吗?

当然。问题与 没有任何关系WhenAny。相反,问题在于代码假定方法是同步的,但它是异步的。

这是一个相对容易犯的错误。但作为一般规则,具有异步签名的方法可能是异步的;它不必异步的。

正如我在博客中所描述的,异步方法开始同步执行,就像同步方法一样。只有当它们命中时,await它们才可以异步运行(即使如此,它们也可以同步继续)。

因此,新版本的LongCompuationAsync是同步的,它在将任务返回到 之前执行整个方法StartAsync,然后将任务传递给WhenAny