Почему незапланированная блокировка задач - PullRequest
0 голосов
/ 10 апреля 2019

Я запускаю 2 задачи, без await -ing их, и одно из них зависит от другого.Я пытаюсь понять, почему следующий фрагмент кода блокируется следующим образом.

public class Tasks {
        EventWaitHandle handle = new EventWaitHandle(false, EventResetMode.ManualReset);

        public async Task Job1() {
            Console.WriteLine("Finished job1");
            handle.Set();
        }

        public async Task Job2() {
            handle.WaitOne();
            Console.WriteLine("Doing Job2 work");
        }
    }

    class Program {
        static async Task Main(string[] args) {

                Tasks seq = new Tasks();
                var t2 =seq.Job2();
                var t1 =seq.Job1();

                await Task.WhenAll(t1, t2);
                Console.WriteLine("finished both");
        }
    }

Если я создаю CPU-bound задач для обеих моих задач, это работает:

var t2=Task.Run(seq.Job2);
var t1=Task.Run(seq.Job1);

Iтакже пытались поместить обе задачи в отдельную задачу из основного потока, и она по-прежнему блокирует:

var bigtask=Task.Run(async()=>{
                         var t2 =seq.Job2();
                         var t1 =seq.Job1();
                     });

Если я запускаю задачу без ожидания, это не то же самое, что запуск новогоCPU-bound задача?(Task.Run)

Ответы [ 2 ]

2 голосов
/ 10 апреля 2019

Посмотрите на предупреждения вашего компилятора;они скажут вам точно, что идет не так.В частности, вы используете async без await, поэтому эти методы будут запускаться синхронно .

Task.Run выполняет метод в потоке пула потоков, что препятствует его запускусинхронно.

Если я запускаю задачу без ожидания, разве это не то же самое, что [использование Task.Run]?

Every async метод начинает выполняться синхронно ;await - это точка, в которой он может вести себя асинхронно.

async сам по себе не использует никакие потоки (или пул потоков);это больше похоже на причудливый синтаксис для обратных вызовов.Task.Run использует пул потоков.

Для решения вашей основной проблемы (когда одна задача ожидает другую), самый простой подход - передать Task, возвращенный из Job1, в метод Job2 и получить Job2 await эту задачу.Если это невозможно, вам нужен асинхронный тип сигнала (не блокирующий, как EventWaitHandle).Одноразовый асинхронный сигнал TaskCompletionSource<T>;SemaphoreSlim также поддерживает асинхронное ожидание;и более сложные координационные примитивы являются частью моей библиотеки AsyncEx .

1 голос
/ 11 апреля 2019

Объявление методов «async» не делает ваш код многопоточным.Вы можете предположить, что ваш код будет работать синхронно до тех пор, пока что-то «ожидается».

Проблема здесь в том, что Job2 никогда не вернется, поэтому ваш код застрянет.Но, например (а не фактическое решение), если вы сделали что-то вроде этого:

            public async Task Job2()
            {
                await Task.Delay(1000);
                handle.WaitOne();
                Console.WriteLine("Doing Job2 work");
            }

Ваша программа фактически завершит работу, потому что функция станет асинхронной и вернется к вызывающей стороне, как только будет задержкаawaited.

Следует избегать использования примитивов синхронизации, таких как "EventWaitHandle / ManualResetEvent", в TPL (async / await), поскольку они физически блокируют поток, а не освобождают его и ожидают обратного вызова.

Вот актуальное решение:

    public class Tasks
    {
        SemaphoreSlim semaphore = new SemaphoreSlim(0);

        public async Task Job1()
        {
            Console.WriteLine("Finished job1");
            semaphore.Release();
        }

        public async Task Job2()
        {
            await semaphore.WaitAsync();
            Console.WriteLine("Doing Job2 work");
        }
    }

    class Program
    {
        static async Task Main(string[] args)
        {

            Tasks seq = new Tasks();
            var t2 = seq.Job2();
            var t1 = seq.Job1();

            await Task.WhenAll(t1, t2);
            Console.WriteLine("finished both");
        }
    }
...