.NET 6 Parallel.ForEachAsync 中需要两个令牌吗?
The need for two tokens in .NET 6 Parallel.ForEachAsync?
我正在试验如何跳出 ForEachAsync
循环。 break
不起作用,但我可以在 CancellationTokenSource 上调用 Cancel
。 ForEachAsync
的签名有两个标记 - 一个作为独立参数,另一个在 Func
正文签名中。
我注意到调用 cts.Cancel()
时,token
和 t
变量都将 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
,它将被取消:
- 关于“外部”cancelation(这就是为什么在调用
cts.Cancel()
时您会看到 t.IsCancellationRequested
设置为 true
)
- 出于内部原因(我发现的原因 - 任何迭代 has thrown an uncaught exception)。
所以传递给 Parallel.ForEachAsync
的 cancellationToken CancellationToken
参数的目的是支持调用者取消和传递给它调用的异步委托的取消 - 支持外部(即调用者)取消) 和内部资源(参见 P.S.).
P.S.
另请注意,通常在您的方法中传递和检查令牌状态是个好主意(即 await someConditionAsync(t)
以及内部相应的实现),因为 CancelationToken
用于所谓的 cooperative cancelation.
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 意味着您可以 'inherit' lambda 之外的变量,但这并不意味着您应该这样做。
我正在试验如何跳出 ForEachAsync
循环。 break
不起作用,但我可以在 CancellationTokenSource 上调用 Cancel
。 ForEachAsync
的签名有两个标记 - 一个作为独立参数,另一个在 Func
正文签名中。
我注意到调用 cts.Cancel()
时,token
和 t
变量都将 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
,它将被取消:
- 关于“外部”cancelation(这就是为什么在调用
cts.Cancel()
时您会看到t.IsCancellationRequested
设置为true
) - 出于内部原因(我发现的原因 - 任何迭代 has thrown an uncaught exception)。
所以传递给 Parallel.ForEachAsync
的 cancellationToken CancellationToken
参数的目的是支持调用者取消和传递给它调用的异步委托的取消 - 支持外部(即调用者)取消) 和内部资源(参见 P.S.).
P.S.
另请注意,通常在您的方法中传递和检查令牌状态是个好主意(即 await someConditionAsync(t)
以及内部相应的实现),因为 CancelationToken
用于所谓的 cooperative cancelation.
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 意味着您可以 'inherit' lambda 之外的变量,但这并不意味着您应该这样做。