在.NET 6 Parallel.ForEachAsync中需要两个取消令牌



我正在试验如何突破ForEachAsync循环。break不起作用,但我可以在CancellationTokenSource上调用CancelForEachAsync的签名有两个令牌——一个作为独立参数,另一个在Func主体签名中。

我注意到,当调用cts.Cancel()时,tokent变量都将IsCancellationRequested设置为true。所以,我的问题是:两个独立的token论点的目的是什么?有什么区别值得注意吗?

List<string> symbols = new() { "A", "B", "C" };
var cts = new CancellationTokenSource();
var token = cts.Token;
token.ThrowIfCancellationRequested();
try
{
await Parallel.ForEachAsync(symbols, token, async (symbol, t) =>
{
if (await someConditionAsync())
{
cts.Cancel();
}
});
catch (OperationCanceledException oce)
{
Console.WriteLine($"Stopping parallel loop: {oce}");
}
finally
{
cts.Dispose();
}

传递给ForEachAsync调用的方法主体的令牌不同,并且来自将被取消的内部CancellationTokenSource

  • 在";外部";cancellation(这就是为什么在调用cts.Cancel()时会看到t.IsCancellationRequested设置为true(
  • 由于内部原因(我发现,任何迭代都抛出了一个未捕获的异常,即应用了故障快速原则(

因此,传递给Parallel.ForEachAsynccancellationToken CancellationToken参数的目的是支持调用者的取消,以及传递给它调用的异步委托的参数——支持外部(即调用者(和内部源的取消(请参阅p.S.(

p.S.

还要注意的是,通常在方法中传递和检查令牌状态是一个好主意(即,内部有相应实现的await someConditionAsync(t)(,因为CancelationToken用于所谓的协作取消。

Parallel.ForEachAsync获取一个令牌,您可以使用该令牌来取消for each(函数的输入(,该令牌还传递给for each的每次迭代(lambda的输入(。

将取消标记传递给lambda的原因之一是避免捕获lambda表达式之外的变量。

想象一下这个代码:

await Parallel.ForEachAsync(symbols, token, async (symbol, t) => MyCode(symbol, t));
Task async MyCode(string symbol, CancellationToken token)
{
if (await someConditionAsync())
{
cts.Cancel();
}
});

以这种方式编写,MyCode无法访问token

使用lambda意味着您可以在lambda之外"继承"变量,但这并不意味着您应该这样做。

最新更新