向本地IAsyncEnumerable函数传取消令牌是否需用EnumeratorCancellation?
异步枚举本地函数中CancellationToken与EnumeratorCancellation特性的疑问
你计划使用的代码模式如下:
public static IAsyncEnumerable<string> MyOuterFunctionAsync( this Client client, CancellationToken cancellationToken, int? limit = null) { return MyLocalFunction(). TakeWhile( (_, index) => limit is null || index < limit.Value); async IAsyncEnumerable<string> MyLocalFunction() { var request = CreateRequest(); do { var page = await request.GetAsync(cancellationToken); foreach (var item in page) { yield return item; } request = GetNextPageRequest(); } while (request is not null) } }
针对你的疑问,解答如下:
1. MyOuterFunctionAsync能否正常工作?
完全可以正常工作。你的本地函数MyLocalFunction直接捕获了外部函数传入的cancellationToken参数,在每次执行await request.GetAsync(cancellationToken)时都会使用这个令牌。当外部的取消令牌触发取消操作时,GetAsync会响应取消逻辑,进而终止整个异步枚举的执行流程,同时TakeWhile的逻辑也会正常生效。
2. 是否需要修改MyLocalFunction的签名添加[EnumeratorCancellation]特性?
不需要,原因如下:
[EnumeratorCancellation]特性的核心作用是:让异步枚举器能够接收调用方通过await foreach (...) WithCancellation(token)方式传入的取消令牌。但在你的代码逻辑里,本地函数的取消令牌是直接从外部函数捕获的,并非通过枚举器的WithCancellation机制传递,因此该特性在这里没有用武之地。- 外部函数
MyOuterFunctionAsync已经直接将cancellationToken作为参数接收,调用方是通过参数传递取消令牌,而非WithCancellation方法,所以给外部函数添加该特性会被提示“无效果”,这是符合预期的。 - 你查看IL未发现差异也印证了这一点:添加该特性不会改变代码的实际执行逻辑,因为当前场景并不依赖它的功能。
如果后续你的场景需要让本地函数接收调用方通过WithCancellation传入的取消令牌,才需要为本地函数的CancellationToken参数添加[EnumeratorCancellation]特性。但就当前的代码模式而言,完全不需要做这个修改。
内容的提问来源于stack exchange,提问作者Zapdor
相关产品推荐
相关产品推荐

