Deutsch
Germany.ruФорумы → Архив Досок→ Программирование

Непонятно с async-await в C# - 2

1777  1 2 3 4 5 все
alex445 свой человек31.08.21 10:48
NEW 31.08.21 10:48 
Последний раз изменено 31.08.21 10:50 (alex445)

Удалите эту тему, пожалуйста Непонятно с async-await в C# - Программирование (germany.ru)


Вот задачка - что выведет код? Можно без временных меток (их для удобства просто добавил) - просто в какой последовательности сообщения будут?


class Program
{
    static DateTime _startTime;

    static async Task Main(string[] args)
    {
        _startTime = DateTime.Now;

        var wait1 = Wait1();
        var wait2 = Wait2();
        var wait3 = Wait3();
        await wait1;
        Console.WriteLine("after wait1");
        await wait2;
        Console.WriteLine("after wait2");
        await wait3;
        Console.WriteLine("after wait3");
    }

    static async Task Wait1()
    {
        await Task.Delay(3000);
        Console.WriteLine($"wait1 is ready {(DateTime.Now - _startTime).Seconds}");
    }

    static async Task Wait2()
    {
        await Task.Delay(2000);
        Console.WriteLine($"wait2 is ready {(DateTime.Now - _startTime).Seconds}");
    }

    static async Task Wait3()
    {
        await Task.Delay(1000);
        Console.WriteLine($"wait3 is ready {(DateTime.Now - _startTime).Seconds}");
    } 
}


У меня выводит


wait3 is ready 1

wait2 is ready 2

wait1 is ready 3

after wait1

after wait2

after wait3


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


wait3 is ready 1

after wait3

wait2 is ready 2

after wait2

wait1 is ready 3

after wait1

#1 
alex445 свой человек31.08.21 10:56
NEW 31.08.21 10:56 
в ответ alex445 31.08.21 10:48, Последний раз изменено 31.08.21 11:02 (alex445)

Варинаты с разным временем ожидания

--------------------

1000

3000

2000

wait1 is ready 1

after wait1

wait3 is ready 2

wait2 is ready 3

after wait2

after wait3

------------------

3000

1000

2000

wait2 is ready 1

wait3 is ready 2

wait1 is ready 3

after wait1

after wait2

after wait3

-------------------

2000

3000

1000

wait3 is ready 1

wait1 is ready 2

after wait1

wait2 is ready 3

after wait2

after wait3

---------------

2000

1000

3000

wait2 is ready 1

wait1 is ready 2

after wait1

after wait2

wait3 is ready 3

after wait3

#2 
alex445 свой человек31.08.21 11:03
31.08.21 11:03 
в ответ alex445 31.08.21 10:56, Последний раз изменено 31.08.21 11:18 (alex445)

Когда бы ни закончились ожидаемые таски, код между ними всегда выполняется в том порядке, как написан


after wait1

after wait2

after wait3


Причём первая строка выполняется лишь после того, как закончится таска, ожидаемая перед этой первой строкой, независимо от того, закончились ли последующие таски.

Я тут вообще логики не вижу.


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


Т. е. либо надо обязательно использовать ContinueWith, либо WhenAll, WhenAny и т.п. вспомогательные методы. Тот поток выполнения, что в вышеприведённом примере - нихрена не логичен.

#3 
NightWatch коренной житель31.08.21 11:10
NightWatch
NEW 31.08.21 11:10 
в ответ alex445 31.08.21 11:03, Последний раз изменено 31.08.21 11:11 (NightWatch)

https://docs.microsoft.com/en-us/dotnet/csharp/language-re...

The await operator suspends evaluation of the enclosing async method until the asynchronous operation represented by its operand completes. When the asynchronous operation completes, the await operator returns the result of the operation, if any. When the await operator is applied to the operand that represents an already completed operation, it returns the result of the operation immediately without suspension of the enclosing method. The await operator doesn't block the thread that evaluates the async method. When the await operator suspends the enclosing async method, the control returns to the caller of the method.

#4 
alex445 свой человек31.08.21 11:20
NEW 31.08.21 11:20 
в ответ NightWatch 31.08.21 11:10

Получается, что код, который содержит в себе подряд несколько await, выполняется последовательно, а параллельно выполняются только ожидаемые (под await) операции?

#5 
alex445 свой человек31.08.21 11:23
NEW 31.08.21 11:23 
в ответ NightWatch 31.08.21 11:10

А если я хочу добавить после каждого "await Task" код, который должен выполниться немедленно после завершения этой Task? То писать как я - просто этот код, который должен выполниться, после каждого await - нельзя?

#6 
NightWatch коренной житель31.08.21 11:25
NightWatch
NEW 31.08.21 11:25 
в ответ alex445 31.08.21 11:20

Применительно к твоему примеру - да.

#7 
AlexNek патриот31.08.21 11:33
AlexNek
NEW 31.08.21 11:33 
в ответ alex445 31.08.21 11:03
Я тут вообще логики не вижу.

Не нужно просто вызывать асинхронные методы синхронно.

Поиграйтесь лучше с этим кодом.

class Program
    {
        private static DateTime _startTime;

        static async Task Main(string[] args)
        {
            _startTime = DateTime.Now;

            await Wait1();
            Console.WriteLine("after wait1");
            await Wait2();
            Console.WriteLine("after wait2");
            await Wait3();
            Console.WriteLine("after wait3");
        }

        private static async Task Wait1()
        {
            await Task.Delay(3000);
            Console.WriteLine($"wait1 is ready {(DateTime.Now - _startTime).Seconds}");
        }

        private static async Task Wait2()
        {
            await Task.Delay(2000);
            Console.WriteLine($"wait2 is ready {(DateTime.Now - _startTime).Seconds}");
        }

        private static async Task Wait3()
        {
            await Task.Delay(1000);
            Console.WriteLine($"wait3 is ready {(DateTime.Now - _startTime).Seconds}");
        }
    }
#8 
NightWatch коренной житель31.08.21 11:34
NightWatch
NEW 31.08.21 11:34 
в ответ alex445 31.08.21 11:23
код, который должен выполниться немедленно после завершения этой Task

То есть, независимо от других задач? -> https://docs.microsoft.com/en-us/dotnet/api/system.threadi...

#9 
alex445 свой человек31.08.21 11:51
NEW 31.08.21 11:51 
в ответ NightWatch 31.08.21 11:34
код, который должен выполниться немедленно после завершения этой Task

То есть, независимо от других задач? -> https://docs.microsoft.com/en-us/dotnet/api/system.threadi...

Да-да, я так и подумал - нужно использовать вспомогательные методы.

#10 
alex445 свой человек31.08.21 11:56
NEW 31.08.21 11:56 
в ответ AlexNek 31.08.21 11:33

Не нужно просто вызывать асинхронные методы синхронно.

Поиграйтесь лучше с этим кодом.

Ну тут вообще всё последовательно. У меня хотя бы таски запускаются параллельно, но ожидаются последовательно - т.е. общее время выполнения всех тасков не больше времени для самой долгой таски. А у вас всё запусается и ожидается последовательно, поэтому общее время выполнения всех тасков всегда суммируется.

#11 
AlexNek патриот31.08.21 14:32
AlexNek
NEW 31.08.21 14:32 
в ответ alex445 31.08.21 11:56

А какая стоит задача то?

С этим кодом может быть примерно такое извращение для паралельного исполнения

        private static  void  RunParallel()
        {
            Task[] tasks = new Task[3];
            tasks[0] = Task.Run(async () => { await Wait1(); });
            tasks[1] = Task.Run(async () => { await Wait1(); });
            tasks[2] = Task.Run(async () => { await Wait1(); });
            Task.WaitAll(tasks);
        }

А так хотя бы сюды нужно глянуть

https://devblogs.microsoft.com/pfxteam/await-synchronizationcontext-and-console-apps/

#12 
alex445 свой человек31.08.21 16:17
NEW 31.08.21 16:17 
в ответ AlexNek 31.08.21 14:32
С этим кодом может быть примерно такое извращение для паралельного исполнения

Тошда уж Parallel - всё уже придумано до нас


Parallel.Invoke(yourActions);

#13 
alex445 свой человек31.08.21 16:27
NEW 31.08.21 16:27 
в ответ AlexNek 31.08.21 14:32

А какая стоит задача то?
А так хотя бы сюды нужно глянуть

https://devblogs.microsoft.com/pfxteam/await-synchronizati...

Я даже эту штуку пару раз проштудировал. А толку? Если не юзаешь постоянно, то вылетает из головы.


А так я играюсь с кодом. Я к этому async await уже раза три подходил, и каждый раз какой-то затык. Там куча подходов и паттернов применяется, и их местами смешивают. Даётся много свободы, но при этом правильных подходов не так много. Зато есть огромное число возможностей сделать неправильно. В этом и сложность. В этом отличие от коллбэчного подхода, где делается только вот так и никак иначе - не ошибёшься.


В МСДН написано, что "асинхронный" подход сделан, чтобы упростить многопоточное программирование по сравнению с "коллбэчным", но по факту они только больше всё запутали - без большого опыта и знания всех подводных камней легко выстрелить себе в ногу.

#14 
alex445 свой человек31.08.21 16:34
NEW 31.08.21 16:34 
в ответ alex445 31.08.21 16:27

Вообще, у меня ещё отсюда тянется c# - How to close modal window after a Task completes? - Stack Overflow

Вроде, решения подходящие мне привели, но я так и не получил понимания, как самому до этого дойти. Тасуя туда-сюда метод закрытия окна, и не понимаю, почему он не срабатывает в определённых случаях, хотя вроде должен.


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

#15 
AlexNek патриот31.08.21 16:40
AlexNek
NEW 31.08.21 16:40 
в ответ alex445 31.08.21 16:17
тогда уж Parallel

Увы, там можно только синхронные методы пользовать


            Parallel.Invoke(
                 () => {  Wait1(); },
                 () => {  Wait2(); },
                 () => {  Wait3(); }
            );
#16 
AlexNek патриот31.08.21 16:48
AlexNek
NEW 31.08.21 16:48 
в ответ alex445 31.08.21 16:27
Если не юзаешь постоянно, то вылетает из головы.

Безусловно. Но нужен пинок поначалу. Мне вот это видео понравилось


#17 
AlexNek патриот01.09.21 21:56
AlexNek
NEW 01.09.21 21:56 
в ответ AlexNek 31.08.21 16:48

Вот еще немного дровишек в костерчик спок

#18 
alex445 свой человек02.09.21 11:18
NEW 02.09.21 11:18 
в ответ AlexNek 31.08.21 16:48

По первому видео возник вопрос. Вот картинка оттуда



Некий внешний код вызывает эту функцию, выполнение функции продолжается до await, если ожидаемая при await операция не завершена, выполнение возвращается в код, вызвавший показанную функцию. Далее выполнение продолжается во внешнем коде, но когда операция при await завершается, выполнение во внешнем коде блокируется и продолжается в данной функции после await?


Если да, то получается, что каждое завершение ожидания задачи в вызванной асинхронной функции прерывает вызвавший эту функцию код? Напишу много await - вызывающий код будет столько же раз прерван (если ожидаемые задачи не выполнены к коменту ожидания)?

#19 
AlexNek патриот02.09.21 13:31
AlexNek
NEW 02.09.21 13:31 
в ответ alex445 02.09.21 11:18
вызывающий код будет столько же раз прерван

прерывание какое-то странное слово.

Попробуйте не в консоли сделать асинк фунцию с Delay и такую же, только чтобы она это время что то исполняла.

#20 
1 2 3 4 5 все