[英]CancellationTokenSource.Cancel() hangs
当异步之一处于活动循环中时,我观察到CancellationTokenSource.Cancel
挂起。
完整代码:
static async Task doStuff(CancellationToken token)
{
try
{
// await Task.Yield();
await Task.Delay(-1, token);
}
catch (TaskCanceledException)
{
}
while (true) ;
}
static void Main(string[] args)
{
var main = Task.Run(() =>
{
using (var csource = new CancellationTokenSource())
{
var task = doStuff(csource.Token);
Console.WriteLine("Spawned");
csource.Cancel();
Console.WriteLine("Cancelled");
}
});
main.GetAwaiter().GetResult();
}
打印Spawned
并挂起。 调用栈看起来像:
ConsoleApp9.exe!ConsoleApp9.Program.doStuff(System.Threading.CancellationToken token) Line 23 C#
[Resuming Async Method]
[External Code]
ConsoleApp9.exe!ConsoleApp9.Program.Main.AnonymousMethod__1_0() Line 34 C#
[External Code]
await Task.Yield
将在输出中产生Spawned\\nCancelled
。
有什么想法吗? C#是否保证曾经屈服的异步永远不会阻止其他异步?
CancellationTokenSource
没有任务计划程序的任何概念。 如果未在自定义同步上下文中注册回调,则CancellationTokenSource将在与.Cancel()
相同的调用堆栈中执行该回调。 在您的情况下,取消回调将完成Task.Delay
返回的任务,然后内联该继续,从而在CancellationTokenSource.Cancel
内部导致无限循环。
您的Task.Yield
示例仅适用于竞争条件。 取消令牌后,线程尚未开始执行Task.Delay
,因此没有继续进行内联的操作。 如果将Main
更改为添加暂停,则即使使用Task.Yield
,它也会冻结:
static void Main(string[] args)
{
var main = Task.Run(() =>
{
using (var csource = new CancellationTokenSource())
{
var task = doStuff(csource.Token);
Console.WriteLine("Spawned");
Thread.Sleep(1000); // Give enough time to reach Task.Delay
csource.Cancel();
Console.WriteLine("Cancelled");
}
});
main.GetAwaiter().GetResult();
}
现在,保护对CancellationTokenSource.Cancel
的调用的唯一可靠方法是将其包装在Task.Run
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.