是什么导致了僵局?
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
这就是你陷入僵局的原因:
执行 Worker()
的线程被 tcs.SetResult(null)
调用阻塞。
执行 Stop()
的线程被 _thread.Join()
调用阻塞。
我的一段代码遇到死锁问题。值得庆幸的是,我已经能够在下面的示例中重现该问题。 运行 作为普通的 .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
这就是你陷入僵局的原因:
执行
Worker()
的线程被tcs.SetResult(null)
调用阻塞。执行
Stop()
的线程被_thread.Join()
调用阻塞。