CancellationToken 阻塞线程执行
CancellationToken blocks thread execution
我正在尝试使用取消标记从另一个线程中跳出循环:
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 上的,但它似乎不依赖于框架。
有人可以帮助我了解发生了什么吗?
问题是 t1.Wait();
(Wait()
是 几乎总是 的问题;p)- 这是偷线程。使用 await t1;
代替,一切都会正常。实际上,取消 需要调用一些回调(取消状态中的延续),然后 return 调用取消的任何内容,并且您在取消中插入了一个阻塞.
这个僵局几乎和那个 described on my blog 一样。您需要注意的一件事是 CancellationTokenSource.Cancel
在这种情况下是 运行 调用 Cancel
的线程上的取消标记延续 。 =27=]
所以,这是正在发生的事情:
Main
开始 LongProcess
.
LongProcess
使用一些 await
并在线程池线程上恢复。
- 与此同时,
Main
正在做 await Task.Delay
。
- 当
LongProcess
执行 Cancel
时,它实际上在当前线程
上恢复执行 Main
方法 。您可以通过在 catch
块中放置一个断点并查看调用堆栈来验证这一点。
当前线程(线程池线程)随后阻塞任务 (.Wait()
)。
现在,LongProcess
无法完成,因为线程当前 运行 已被阻塞等待完成。
要修复它,请将 Wait()
更改为 await
。
我正在尝试使用取消标记从另一个线程中跳出循环:
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 上的,但它似乎不依赖于框架。 有人可以帮助我了解发生了什么吗?
问题是 t1.Wait();
(Wait()
是 几乎总是 的问题;p)- 这是偷线程。使用 await t1;
代替,一切都会正常。实际上,取消 需要调用一些回调(取消状态中的延续),然后 return 调用取消的任何内容,并且您在取消中插入了一个阻塞.
这个僵局几乎和那个 described on my blog 一样。您需要注意的一件事是 所以,这是正在发生的事情: 要修复它,请将 CancellationTokenSource.Cancel
在这种情况下是 运行 调用 Cancel
的线程上的取消标记延续 。 =27=]
上恢复执行 Main
开始 LongProcess
.LongProcess
使用一些 await
并在线程池线程上恢复。Main
正在做 await Task.Delay
。LongProcess
执行 Cancel
时,它实际上在当前线程 Main
方法 。您可以通过在 catch
块中放置一个断点并查看调用堆栈来验证这一点。
.Wait()
)。LongProcess
无法完成,因为线程当前 运行 已被阻塞等待完成。Wait()
更改为 await
。