[英]What is the use of Cancellation token here
我有以下代码块:
using (var cancelSource = new CancellationTokenSource())
{
Task[] tasks = null;
var cancelToken = cancelSource.Token;
tasks = new[]
{
Task.Run(async () => await ThrowAfterAsync("C", cancelToken, 1000)) //<---
};
await Task.Delay(howLongSecs * 1000); // <---
cancelSource.Cancel();
await Task.WhenAll(tasks);
}
其中ThrowAfterAsync
具有以下内容:
private async Task ThrowAfterAsync(string taskId, CancellationToken cancelToken, int afterMs)
{
await Task.Delay(afterMs, cancelToken);
var msg = $"{taskId} throwing after {afterMs}ms";
Console.WriteLine(msg);
throw new ApplicationException(msg);
}
Resharper建议我可以将Task.Run()
的重载与取消令牌一起使用,如下所示:
Task.Run(async () => await ThrowAfterAsync("C", cancelToken, 1000), cancelToken)
但为什么 ? 在没有取消标记作为参数的情况下,与第一个版本相比,这样做有什么好处?
在这种特定情况下,没有任何意义。 通常,您会按照建议的方式进行操作,因为通过将令牌传递给Task.Run
,即使在操作有机会开始之前取消了令牌,它也可以避免甚至排定调度操作的开始 。您正在创建令牌的情况下,您知道在开始操作时不会取消它。
但是,您不需要将令牌传递给Task.Run
的原因是,启动该任务的代码是负责取消令牌的操作 ,因此它知道令牌尚未取消。 通常,您会从其他地方接受令牌,并且您不知道它是否/何时被取消。
所有这一切说,没有理由使用,甚至Task.Run
可言 。 您可以这样写:
tasks = new[] { ThrowAfterAsync("C", cancelToken, 1000) };
它将具有相同的行为,但是不必为了启动异步操作而不必要地启动新线程。
接下来,由于操作代码的结构,即使操作在此之前完成,您的代码也永远不会在howLongSecs
秒内返回 。 您应该简单地将超时提供给取消令牌源,并让它在正确的时间取消令牌,如果操作在取消发生之前完成,则不会延迟其余方法,因此您的整个方法可以只是写为:
using (var cancelSource = new CancellationTokenSource(Timespan.FromSeconds(howLongSecs)))
{
await ThrowAfterAsync("C", cancelToken, 1000)
}
Resharper看到您正在使用的方法( Task.Run
)的重载接受CancellationToken
,作用域中具有CancellationToken
实例,但是您不使用接受令牌的重载方法。 它不会对您的代码进行任何广泛的分析-就是这么简单。 您可以使用以下代码轻松验证这一点:
class Program {
static void Main() {
CancellationToken ct;
Test("msg"); // will suggest to pass CancellationToken here
}
private static void Test(string msg) {
}
private static void Test(string msg, CancellationToken ct) {
}
}
是的,代码本身很奇怪,您根本不需要将异步包装在Task.Run
中,但是我不会碰到这一点,因为您只是问为什么Resharper建议这样做。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.