我在我的一段代码中遇到了死锁问题。值得庆幸的是,我已经能够在下面的例子中重现这个问题。作为普通的.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
解决了问题,而且根本没有问题。我只想了解问题所在。
答案 0 :(得分:3)
tcs.SetResult(null);
中的Worker()
调用将不会返回(有关详细信息,请查看this question)。在您的情况下,任务状态为WaitingForActivation
,这就是您遇到死锁的原因:
线程执行Worker()
被tcs.SetResult(null)
电话阻止。
线程执行Stop()
被_thread.Join()
电话阻止。
答案 1 :(得分:0)
因为MainAsync()
线程比其他线程'更快'。
并且您只对不在线程上的任务进行控制!
在方法MainAsync()
中,等待方法StartAsync()
完成其工作,然后启动线程。方法StartAsync()
完成其工作(创建并启动线程)后,该函数会通知MainAsync()
完成其工作。然后MainAsync()
调用Stop方法。
但你的线程在哪里?它并行运行,没有任何控制,并试图完成其工作。这不是死锁,任务和线程之间没有同步。
这就是为什么当你放置await Task.Delay(1)
代码时,因为线程足以在任务结束之前完成工作(thread.join)。