Что вызывает тупик?
Я сталкиваюсь с тупиковой проблемой в моем коде. К счастью, я смог воспроизвести проблему в следующем примере. Запустите как обычное консольное приложение.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
и проблем нет вообще. Я только хотел бы понять, где проблема.
2 ответа
tcs.SetResult(null);
вызывать Worker()
не вернется, пока основная задача не будет завершена (проверьте этот вопрос для деталей). В вашем случае статус задачи WaitingForActivation
вот почему вы зашли в тупик:
Выполнение потока
Worker()
заблокированtcs.SetResult(null)
вызов.Выполнение потока
Stop()
заблокирован_thread.Join()
вызов.
Так как MainAsync()
поток "быстрее", чем другой поток. И вы управляете только задачами, а не потоками!
В вашем методе MainAsync()
ты ждешь метод StartAsync()
чтобы закончить свою работу, а затем вы начинаете поток. Однажды метод StartAsync()
сделано с его работой (созданный и запущенный поток), что функция сообщает MainAsync()
о завершении своей работы. затем MainAsync()
вызывает метод Stop. Но где ваша нить? Он работает параллельно без какого-либо контроля и пытается завершить свою работу. Это не тупик, нет синхронизации между задачей и потоком.
Вот почему, когда вы кладете await Task.Delay(1)
Ваш код работает, потому что поток достаточно быстр, чтобы завершить работу до того, как задача завершит его (thread.join).