CancellationToken阻塞线程执行
c#
我正在尝试使用取消令牌从另一个线程中跳出循环:
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 上运行它,但它似乎不依赖于框架。有人可以帮助我了解发生了什么吗?
回答
这种僵局几乎与我博客中描述的僵局一模一样。您需要注意的一件事是,CancellationTokenSource.Cancel在这种情况下,在调用Cancel.
所以,这就是正在发生的事情:
Main开始LongProcess。LongProcess使用 someawaits 并在线程池线程上恢复。- 与此同时,
Main正在做一个await Task.Delay. - 什么
LongProcess时候执行Cancel,它实际上会继续在当前线程上执行该Main方法。您可以通过在块中放置断点并查看调用堆栈来验证这一点。catch - 当前线程(线程池线程)然后阻塞在任务 (
.Wait()) 上。 - 现在,
LongProcess无法完成,因为当前运行它的线程被阻塞,等待它完成。
要修复它,请将 更改Wait()为await。