我一直在开发一个将耗时任务排队到通道中的功能,我使用以下方式来迭代通道: await foreach(var item in channel.Reader.ReadAllAsync(cancellationToken)) {...}
我原本以为,当通过cancellationToken
请求取消时,ReadAllAsync
会在随后的第一次迭代中抛出异常。
但实际上,情况并非如此。该循环会一直执行,直到处理完所有的项目,然后才会抛出OperationCanceledException
异常。
这看起来有点奇怪。从ChannelReader
的github repo中可以看到,取消令牌标记了[EnumeratorCancellation]
属性,因此应该将其传递给围绕yield return item;
生成的状态机 (如果我错了,请纠正我)。
我的问题是,这是ReadAllAsync(CancellationToken)
的(有些)正常行为,还是我遗漏了什么?
var channel = Channel.CreateUnbounded<int>();
for (int i = 1; i <= 10; i++) channel.Writer.TryWrite(i);
int itemsRead = 0;
var cts = new CancellationTokenSource();
try
{
await foreach (var i in channel.Reader.ReadAllAsync(cts.Token))
{
Console.WriteLine($"Read item: {i}. Requested cancellation: " +
$"{cts.Token.IsCancellationRequested}");
if (++itemsRead > 4 && !cts.IsCancellationRequested)
{
Console.WriteLine("Cancelling...");
cts.Cancel();
}
}
}
catch (OperationCanceledException)
{
Console.WriteLine($"Operation cancelled. Items read: {itemsRead}");
}
以下是上述内容的输出。请注意,在应该在中途取消后,项目获取仍在继续:
Read item: 1. Requested cancellation: False
Read item: 2. Requested cancellation: False
Read item: 3. Requested cancellation: False
Read item: 4. Requested cancellation: False
Read item: 5. Requested cancellation: False
Cancelling...
Read item: 6. Requested cancellation: True
Read item: 7. Requested cancellation: True
Read item: 8. Requested cancellation: True
Read item: 9. Requested cancellation: True
Read item: 10. Requested cancellation: True
Operation cancelled. Items read: 10
await foreach
循环体)应该检查取消令牌。 - Stephen Cleary