如何解决CancellationToken 阻塞线程执行
using System;
using System.Threading;
using System.Threading.Tasks;
namespace CancellationTokenTest
{
class Program
{
private static CancellationTokenSource token;
static async Task Main(string[] args)
{
token = new CancellationTokenSource();
var t1 = LongProcess1();
for (int i = 0; i < 5; i++)
{
try
{
await Task.Delay(2000,token.Token);
Console.WriteLine($"Main awaited {i}");
}
catch (OperationCanceledException)
{
break;
}
}
Console.WriteLine("Main loop completed");
t1.Wait();
Console.WriteLine("Application end");
}
static async Task LongProcess1()
{
for (int i = 0; i < 5; i++)
{
await Task.Delay(1000);
Console.WriteLine($"LongProcess1 awaited {i}");
}
Console.WriteLine("Submitting cancel");
token.Cancel(); // <------ Blocking execution
Console.WriteLine("Cancellation done!");
}
}
}
问题在于 Console.WriteLine("Cancellation done!");
行永远不会执行,Main()
方法一直在等待 t1
完成。
我不明白为什么!
我在 .NET 5 上运行它,但它似乎不依赖于框架。 有人可以帮我了解发生了什么吗?
解决方法
这种僵局几乎与described on my blog 的僵局一模一样。您需要注意的一件事是,在这种情况下,CancellationTokenSource.Cancel
在调用 Cancel
的线程上运行取消令牌延续。
所以,这是发生了什么:
-
Main
开始于LongProcess
。 -
LongProcess
使用一些await
并在线程池线程上恢复。 - 与此同时,
Main
正在执行await Task.Delay
。 - 当
LongProcess
执行Cancel
时,它实际上会继续在当前线程上执行Main
方法。您可以通过在catch
块中放置断点并查看调用堆栈来验证这一点。 - 当前线程(线程池线程)然后阻塞在任务 (
.Wait()
) 上。 - 现在,
LongProcess
无法完成,因为当前运行它的线程被阻塞,等待它完成。
要修复它,请将 Wait()
更改为 await
。
问题是t1.Wait();
(Wait()
几乎总是问题;p) - 这是在窃取线程。改用 await t1;
,一切都会好起来的。实际上,cancellation 需要调用一些回调(处于取消状态的延续),然后返回到执行取消的任何操作,并且您已经在取消中插入了阻塞。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。