是什么导致了僵局?

What's causing a deadlock?

我的一段代码遇到死锁问题。值得庆幸的是,我已经能够在下面的示例中重现该问题。 运行 作为普通的 .Net Core 2.0 控制台应用程序。

class Class2
{

    static void Main(string[] args)
    {
        Task.Run(MainAsync);
        Console.WriteLine("Press any key...");
        Console.ReadKey();
    }

    static async Task MainAsync()
    {
        await StartAsync();
        //await Task.Delay(1);  //a little delay makes it working
        Stop();
    }


    static async Task StartAsync()
    {
        var tcs = new TaskCompletionSource<object>();
        StartCore(tcs);
        await tcs.Task;
    }


    static void StartCore(TaskCompletionSource<object> tcs)
    {
        _cts = new CancellationTokenSource();
        _thread = new Thread(Worker);
        _thread.Start(tcs);
    }


    static Thread _thread;
    static CancellationTokenSource _cts;


    static void Worker(object state)
    {
        Console.WriteLine("entering worker");
        Thread.Sleep(100);  //some work

        var tcs = (TaskCompletionSource<object>)state;
        tcs.SetResult(null);

        Console.WriteLine("entering loop");
        while (_cts.IsCancellationRequested == false)
        {
            Thread.Sleep(100);  //some work
        }
        Console.WriteLine("exiting worker");
    }


    static void Stop()
    {
        Console.WriteLine("entering stop");
        _cts.Cancel();
        _thread.Join();
        Console.WriteLine("exiting stop");
    }

}

我期望的是完整的序列如下:

Press any key...
entering worker
entering loop
entering stop
exiting worker
exiting stop

但是,实际序列在 Thread.Join 调用时停止:

Press any key...
entering worker
entering stop

最后,如果我在 MainAsync 正文中插入一个小的延迟,一切都会顺利进行。 为什么(在哪里)发生死锁?

注意:在原始代码中,我使用 SemaphoreSlim 而不是 TaskCompletionSource 解决了问题,完全没有问题。我只想了解问题出在哪里

因为 MainAsync() 线程比其他线程 'faster'。 而且您只控制任务而不是 threads!

在您的方法 MainAsync() 中,您等待方法 StartAsync() 完成其工作,然后启动线程。一旦方法 StartAsync() 完成其工作(创建并启动线程),该函数将通知 MainAsync() 完成其工作。然后 MainAsync() 调用 Stop 方法。 但是你的线程在哪里?它在没有任何控制的情况下并行运行,并试图完成它的工作。这不是死锁,任务和线程之间没有同步。

这就是为什么当您放置 await Task.Delay(1) 时您的代码可以工作,因为线程足够快,可以在任务结束之前完成工作 (thread.join)。

tcs.SetResult(null);Worker() 中的调用不会 return 直到基础任务完成(查看 this question 了解详情)。在你的情况下,任务状态是 WaitingForActivation 这就是你陷入僵局的原因:

  1. 执行 Worker() 的线程被 tcs.SetResult(null) 调用阻塞。

  2. 执行 Stop() 的线程被 _thread.Join() 调用阻塞。