WhenAny 在某些情况下表现得像 WhenAll
WhenAny behaving like WhenAll in certain case
所以我遇到了第三方库的问题,即使调用 cancellationToken.Cancel
,调用也可能会卡住并且永远不会 return。下面是一个处理这种情况的原型,它是有效的。
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";
}
所以 Above 会等待 3 秒,然后它会像它应该的那样抛出一个 TaskCancelledException
。
如果您随后将方法 LongComputationAsync
更改为以下内容:
private Task<string> LongComputationAsync()
{
Task.Delay(1 * 60 * 1000).ConfigureAwait(false).GetAwaiter().GetResult();
return Task.FromResult("Done");
}
我仍然希望它具有相同的行为,但它的作用是等待整整 1 分钟(在 LongComputationAsync()
中指定),然后抛出 TaskCancelledException
。
谁能给我解释一下?关于这是如何工作的,或者这是否是正确的开始行为。
Can anyone explain this to me?
当然可以。该问题与 WhenAny
无关。相反,问题在于代码假定同步方法是异步的。
这是一个相对容易犯的错误。但作为一般规则,具有异步签名的方法 可能 是异步的;它没有是异步的。
正如我在我的博客中所描述的,asynchronous methods begin executing synchronously, just like synchronous methods。只有当他们命中 await
时,他们才可能 运行 异步(即便如此,他们也可能会继续同步)。
因此,新版本的 LongCompuationAsync
是同步的,它在将任务返回给 StartAsync
之前执行整个方法,然后将其传递给 WhenAny
.
所以我遇到了第三方库的问题,即使调用 cancellationToken.Cancel
,调用也可能会卡住并且永远不会 return。下面是一个处理这种情况的原型,它是有效的。
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";
}
所以 Above 会等待 3 秒,然后它会像它应该的那样抛出一个 TaskCancelledException
。
如果您随后将方法 LongComputationAsync
更改为以下内容:
private Task<string> LongComputationAsync()
{
Task.Delay(1 * 60 * 1000).ConfigureAwait(false).GetAwaiter().GetResult();
return Task.FromResult("Done");
}
我仍然希望它具有相同的行为,但它的作用是等待整整 1 分钟(在 LongComputationAsync()
中指定),然后抛出 TaskCancelledException
。
谁能给我解释一下?关于这是如何工作的,或者这是否是正确的开始行为。
Can anyone explain this to me?
当然可以。该问题与 WhenAny
无关。相反,问题在于代码假定同步方法是异步的。
这是一个相对容易犯的错误。但作为一般规则,具有异步签名的方法 可能 是异步的;它没有是异步的。
正如我在我的博客中所描述的,asynchronous methods begin executing synchronously, just like synchronous methods。只有当他们命中 await
时,他们才可能 运行 异步(即便如此,他们也可能会继续同步)。
因此,新版本的 LongCompuationAsync
是同步的,它在将任务返回给 StartAsync
之前执行整个方法,然后将其传递给 WhenAny
.