При выполнении асинхронного / ожидающего эксперимента операции не выполняются в ожидаемом порядке - PullRequest
0 голосов
/ 14 февраля 2020

Я создал эксперимент (запущенный в XUnit) для проверки вывода. Я использую три метода - тестовый метод, асинхронный метод, который вызывает await, и внутреннюю задачу, которая спит в течение 5 секунд.

    [Fact]
    public void TestAsyncRunning()
    {
        _testOutput.WriteLine("Beginning Test");
        _testOutput.WriteLine("Calling RunningAsync");

        var t = RunningAsync();

        _testOutput.WriteLine("In outer method, after calling async method");

        t.Wait(); //IOW, Thread.Join();

        _testOutput.WriteLine("Exiting Test");
    }


    private async Task RunningAsync()
    {
        _testOutput.WriteLine("RunningAsync: Enter Method");
        _testOutput.WriteLine("RunningAsync: Calling and awaiting inner running");
        await InnerRunning();
        _testOutput.WriteLine("RunningAsync: Returning");   
    }

    private Task InnerRunning()
    {
        _testOutput.WriteLine("InnerRunning: Begin method");
        _testOutput.WriteLine("InnerRunning: Sleeping For 5 seconds");
        var t = Task.Run(() => Thread.Sleep(5000));
        _testOutput.WriteLine("InnerRunning: Wake up and exit");
        return t;
    }

В следующих выходных данных строки со звездочками указывают на неожиданный порядок.

Ожидаемый вывод:

Beginning Test
Calling RunningAsync
RunningAsync: Enter Method
RunningAsync: Calling and awaiting inner running
InnerRunning: Begin method
InnerRunning: Sleeping For 5 seconds
*In outer method, after calling async method
*InnerRunning: Wake up and exit
RunningAsync: Returning
Exiting Test

Вывод, который я получаю

Beginning Test
Calling RunningAsync
RunningAsync: Enter Method
RunningAsync: Calling and awaiting inner running
InnerRunning: Begin method
InnerRunning: Sleeping For 5 seconds
*InnerRunning: Wake up and exit
*In outer method, after calling async method
RunningAsync: Returning
Exiting Test

Похоже, что метод asyn c на самом деле работает asyn c но метод, который он вызывает, работает синхронно. Это связано с TaskScheduler? Я явно что-то упускаю ...….

Ответы [ 4 ]

3 голосов
/ 14 февраля 2020

InnerRunning не является асинхронным. Это синхронный метод, который возвращает Task. Таким образом, эти сообщения всегда будут появляться вместе:

InnerRunning: Begin method
InnerRunning: Sleeping For 5 seconds
InnerRunning: Wake up and exit

Если вы хотите, чтобы InnerRunning асинхронно ожидал делегата Task.Run, вам нужно await выполнить эту задачу в этом методе:

_testOutput.WriteLine("InnerRunning: Begin method");
_testOutput.WriteLine("InnerRunning: Sleeping For 5 seconds");
await Task.Run(() => Thread.Sleep(5000));
_testOutput.WriteLine("InnerRunning: Wake up and exit");

Для получения дополнительной вводной информации о async / await я рекомендую мое async intro .

0 голосов
/ 14 февраля 2020

Хорошо, так что я нашел ответ. Моя проблема заключалась в том, что я поставил линии отладчика не в том месте. Я изменяю самый внутренний метод на

    private Task InnerRunning()
    {

        _testOutput.WriteLine("InnerRunning: Begin method");

        _testOutput.WriteLine("InnerRunning: Starting Task");
        var t = Task.Run(() =>
        {
            _testOutput.WriteLine("InnerRunning Lambda: Sleeping For 5 seconds");
            Thread.Sleep(5000);
            _testOutput.WriteLine("InnerRunning Lambda: Wake up and exit");
        });
        _testOutput.WriteLine("InnerRunning: After Task");

        return t;
    }

И теперь я получаю этот результат:

Beginning Test
Calling RunningAsync
RunningAsync: Enter Method
RunningAsync: Calling and awaiting inner running
InnerRunning: Begin method
InnerRunning: Starting Task
InnerRunning: After Task
InnerRunning Lambda: Sleeping For 5 seconds
In outer method, after calling async method
InnerRunning Lambda: Wake up and exit
RunningAsync: Returning
Exiting Test

Спасибо всем за ваши наводящие на размышления комментарии!

0 голосов
/ 14 февраля 2020

Добавление большего к Athanas ios Катарас и Стивен Клири пост

Вы даже можете увидеть изменение в потоке для входящих и возвращаемых идентификаторов потоков, и этот код будет соответствовать полному асинхронному коду , если вы используете t.Wait (), он просто сразу запускает асинхронные c методы для синхронного запуска:

    public static async Task Main()
    {
        Console.WriteLine("Beginning Test");
        Console.WriteLine("Calling RunningAsync");
        Console.WriteLine("1 " + Thread.CurrentThread.ManagedThreadId);
        await RunningAsync();
        Console.WriteLine("2 " + Thread.CurrentThread.ManagedThreadId);
        Console.WriteLine("In outer method, after calling async method");
        Console.WriteLine("Exiting Test");
    }


    private static async Task RunningAsync()
    {
        Console.WriteLine("RunningAsync: Enter Method");
        Console.WriteLine("RunningAsync: Calling and awaiting inner running");
        Console.WriteLine("3 " + Thread.CurrentThread.ManagedThreadId);
        await InnerRunning();
        Console.WriteLine("4 " + Thread.CurrentThread.ManagedThreadId);
        Console.WriteLine("RunningAsync: Returning");
    }

    private static async Task InnerRunning()
    {

        Console.WriteLine("InnerRunning: Begin method");
        Console.WriteLine("InnerRunning: Sleeping For 5 seconds");
        Console.WriteLine("5 " + Thread.CurrentThread.ManagedThreadId);
        await Task.Run(() => Thread.Sleep(5000));
        Console.WriteLine("6 " + Thread.CurrentThread.ManagedThreadId);
        Console.WriteLine("InnerRunning: Wake up and exit");
    }

Вывод будет:

Calling RunningAsync
1 1
RunningAsync: Enter Method
RunningAsync: Calling and awaiting inner running
3 1
InnerRunning: Begin method
InnerRunning: Sleeping For 5 seconds
5 1
6 4
InnerRunning: Wake up and exit
4 4
RunningAsync: Returning
2 4
In outer method, after calling async method
Exiting Test
0 голосов
/ 14 февраля 2020

Попробуйте изменить свой код следующим образом:

var t = await RunningAsync();

_testOutput.WriteLine("In outer method, after calling async method");

Когда вы не ожидаете асинхронного c метода, внешний будет продолжать работать, особенно когда внутренний ожидает.

Добавление сообщения об ожидании устранит проблему.

...