dB.*_*yee 1 .net c# resharper task task-parallel-library
我有以下代码:
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);
}
Run Code Online (Sandbox Code Playgroud)
哪里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);
}
Run Code Online (Sandbox Code Playgroud)
Resharper建议我可以使用Task.Run()带有取消令牌的重载,如下所示:
Task.Run(async () => await ThrowAfterAsync("C", cancelToken, 1000), cancelToken)
Run Code Online (Sandbox Code Playgroud)
但为什么 ?在没有取消令牌作为参数的情况下,在第一个版本上执行此操作有什么好处?
在这种具体情况下,没有意义.一般来说,你想要按照它的建议去做,因为通过将令牌传递给Task.Run它可以避免在操作甚至有机会启动之前取消令牌时首先安排操作,但在你的情况下你'重新创建令牌,您知道在开始操作时它不会被取消.
但是你不需要传递令牌的原因Task.Run是因为启动该任务的代码是负责取消令牌的操作,因此它知道令牌尚未被取消.通常你会从其他地方接受一个令牌,你不知道它是否/何时被取消.
所有这一切说,没有理由使用,甚至Task.Run 在所有.你可以写:
tasks = new[] { ThrowAfterAsync("C", cancelToken, 1000) };
Run Code Online (Sandbox Code Playgroud)
它将具有相同的行为,但不必为了启动异步操作而不必要地启动新线程.
接下来,您的代码永远不会在少于howLongSecs几秒的时间内返回,即使操作在此之前完成,因为您的代码结构如何.你应该简单地给取消令牌源提供超时并让它在正确的时间处理取消令牌,如果操作在取消之前完成,它将不会延迟你的方法的其余部分,所以你的整个方法可以只写为:
using (var cancelSource = new CancellationTokenSource(Timespan.FromSeconds(howLongSecs)))
{
await ThrowAfterAsync("C", cancelToken, 1000)
}
Run Code Online (Sandbox Code Playgroud)