Как ожидать Task.Run (действие), когда действие неизвестно - PullRequest
0 голосов
/ 22 января 2020

У меня есть следующий код:

  static void Main(string[] args)
    {
        Run(() => LongProcess()).ContinueWith( t => Run(() => LongerProcess()));

        int count = 5;
        do
        {
            Console.WriteLine(count.ToString());
            Thread.Sleep(100);
            count++;
        } while (count < 20);

        Console.ReadKey();
    }

    private static void LongProcess()
    {

        Console.WriteLine("Long Process 1");
        Thread.Sleep(2000);
        Console.WriteLine("Long Process 2");
    }

    private static void LongerProcess()
    {
        Console.WriteLine("Longer Process 1");
        Thread.Sleep(3000);
        Console.WriteLine("Longer Process 2");
    }

    private static async Task Run(Action action)
    {

        await Task.Run(action);  //shouldn't this wait until the action is completed??
                                // how should I modify the code so that the program waits until action is done before returning?

    }

С await Task.Run(action); я ожидаю, что программа будет ждать до тех пор, пока не будет сделано action, прежде чем перейти к следующему шагу. Другими словами, я ожидаю выводов вроде:

Long Process 1
Long Process 2
Longer Process 1
Longer Process 2
5
6
7
8
...
19

Однако вывод выглядит как

Long Process 1
5
6
7
8
...
19
Long Process 2
Longer Process 1
Longer Process 2

Вопросы:

  1. Почему нет await Task.Run ждет, пока action завершится?
  2. Как изменить код, чтобы он выдавал нужный мне вывод?

1 Ответ

4 голосов
/ 22 января 2020

Использование await не ждет, вот и весь смысл. Если вы хотите подождать, вы используете Task.Wait.

Однако выполнение метода async возобновится только после завершения ожидаемой задачи (это продолжение). Давайте посмотрим на ваш метод async:

private static async Task Run(Action action)
{

    await Task.Run(action);
    // There is nothing here
}

Нет кода для выполнения после выполнения задачи ... Хорошо, метод async возвращает задачу, которая завершается, когда она выполнена. Давайте посмотрим, если вы await это ...

Run(() => LongProcess()).ContinueWith( t => Run(() => LongerProcess()));

Вы этого не сделаете. Вы запускаете эту задачу и забываете о ней.


Я думаю, что это то, что вы хотите:

static async void Main(string[] args)
{
    await Run(() => LongProcess());
    await Run(() => LongerProcess());

    int count = 5;
    do
    {
        Console.WriteLine(count.ToString());
        Thread.Sleep(100);
        count++;
    } while (count < 20);

    Console.ReadKey();
}

Примечание : Asyn c Main is a C# 7.1 функция.

Этот код будет запускать задачу, которая вызывает LongProcess, а затем в качестве продолжения задачу, которая запускает LongerProcess, а затем в качестве продолжения остальной код.

Почему вы хотите, чтобы вместо простого синхронного вызова этих методов я был вне себя.

Кстати, в методе async вы можете использовать await Task.Delay(milliseconds) вместо Thread.Sleep(milliseconds). Преимущество в том, что поток не ждет. Это похоже на создание продолжения для одного таймера выполнения.


Если async/await - это просто продолжения, вам может быть интересно, почему люди захотят использовать их вместо ContinueWith .

Позвольте привести несколько причин:

  • Код с async/await легче читать. Менее вложенные вещи, омрачающие код. Вы можете понять, что делает код, независимо от понимания многопоточности.
  • Код с async/await легче писать. Вы просто пишете это так, как если бы все было синхронно с некоторыми async и await, искрившимися там. Вы можете написать синхронный код и потом беспокоиться о том, как сделать его параллельным. Вы также в конечном итоге пишете меньше, что означает, что вы более продуктивны.
  • Код с async/await умнее ™. Компилятор переписывает ваш код как конечный автомат, позволяя вам помещать await внутри операторов без какого-либо кода гимнастики ™. И это распространяется на обработку исключений. Видите ли, в вашем огне и забыли продолжение задачи, вы не обрабатываете исключения. Что делать, если LongerProcess бросает? Что ж, с async/await вы можете просто поместить await внутрь try ... catch, и он делает правильные вещи ™.

Это все хорошее разделение интересов.


Вы можете также задаться вопросом, в какой ситуации лучше использовать async/await вместо синхронного кода. И ответ заключается в том, что он сияет при работе с операциями ввода-вывода. При взаимодействии с внешней системой обычно не сразу получить ответ. Например, если вы хотите читать с диска или загружать из сети.

Фактически, если вы рассматриваете манипулирование формой и обработку ее события как операции ввода-вывода (потому что они есть). Вы увидите, что они являются хорошим местом для использования async/await. В частности, учитывая, что контекст синхронизации для потоков пользовательского интерфейса будет сохранять продолжения в том же потоке по умолчанию. Конечно, вы можете использовать async/await с операциями с привязкой к процессору, используя Task.Run. Когда это хорошая идея? Ах, да, когда вы хотите, чтобы интерфейс реагировал.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...