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使用 some awaits 并在线程池线程上恢复。
  • 与此同时,Main正在做一个await Task.Delay.
  • 什么LongProcess时候执行Cancel,它实际上会继续在当前线程上执行该Main方法。您可以通过在块中放置断点并查看调用堆栈来验证这一点。catch
  • 当前线程(线程池线程)然后阻塞在任务 ( .Wait()) 上。
  • 现在,LongProcess无法完成,因为当前运行它的线程被阻塞,等待它完成。

要修复它,请将 更改Wait()await


以上是CancellationToken阻塞线程执行的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>