Что вызывает тупик?

Я сталкиваюсь с тупиковой проблемой в моем коде. К счастью, я смог воспроизвести проблему в следующем примере. Запустите как обычное консольное приложение.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 вот почему вы зашли в тупик:

  1. Выполнение потока Worker() заблокирован tcs.SetResult(null) вызов.

  2. Выполнение потока Stop() заблокирован _thread.Join() вызов.

Так как MainAsync() поток "быстрее", чем другой поток. И вы управляете только задачами, а не потоками!

В вашем методе MainAsync() ты ждешь метод StartAsync() чтобы закончить свою работу, а затем вы начинаете поток. Однажды метод StartAsync() сделано с его работой (созданный и запущенный поток), что функция сообщает MainAsync() о завершении своей работы. затем MainAsync() вызывает метод Stop. Но где ваша нить? Он работает параллельно без какого-либо контроля и пытается завершить свою работу. Это не тупик, нет синхронизации между задачей и потоком.

Вот почему, когда вы кладете await Task.Delay(1) Ваш код работает, потому что поток достаточно быстр, чтобы завершить работу до того, как задача завершит его (thread.join).

Другие вопросы по тегам