Async/await, There is no thread – легко обмануть тех кто сам обманываться рад

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!

Когда нам показывают на некотором примере, что асинхронная операция не создает потока нам пытаются внушить что асинхронная операция НИКОГДА не создает потока и в принципе не может его создать, но это не правда! Простой пример с работающим кодом доказывает обратное. Давайте разберем этот пример.

Логика тех, кто поддается такому внушению мне вполне понятна, они хотят упростить себе жизнь, сократить объем теории, с которой надо разбираться.

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


Итак, пример кода, который будет создавать дополнительный поток для кода асинхронной операции в консольном (это важно! почему, мы увидим дальше) приложении выглядит так:

        static async Task Main()
        {
            SomeMethodAsync1();
            Console.WriteLine($"ThrID={Thread.CurrentThread.ManagedThreadId}");
            for (int i = 0; i < 5; i++)
            {
                Console.WriteLine($"MAIN Iter={i}");
                Thread.Sleep(100);
            }
        }

        static async Task SomeMethodAsync1()
        {
            await Task.Yield();
            Console.WriteLine($"TrID={Thread.CurrentThread.ManagedThreadId}");
            for (int i = 0; i < 5; i++)
            {
                Console.WriteLine($"MethodIter={i}");
                Thread.Sleep(100);
            }
        }

Этот код генерирует следующий вывод:

Собственно, уже из этого вывода видно, что две функции выполняются в разных потоках с идентификаторами: ID=1 и ID=5.

 Можно рассмотреть эти потоки N1 и N5 более пристально в режиме отладки:

Not Flagged                12268  1          Main Thread   Main Thread            System.Private.CoreLib.dll!System.Threading.Thread.Sleep

Not Flagged    >           26956  5          Worker Thread           .NET ThreadPool Worker            PrimMath.dll!PrimMath.Program.SomeMethodAsync1

Мы видим, что потоки у нас берутся из Тред-пула, то есть, формально говоря, наша функция SomeMethodAsync1() не создает поток, она берет существующий из Тред-пула. Но никак нельзя отрицать тот факт, что асинхронная операция все-таки использует дополнительный поток, то есть утверждение There is no thread оказывается ложным для этого кода, потому что мы явно видим, что The thread with ID5 is definitely present.

Но может быть я где-то, как-то вас тоже обманываю? Если вы не верите мне и моему примеру и своим глазам, вы все также можете обратиться к незабвенному Stephen Toub и к его уже изрядно всем надоевшей (надеюсь это не так :) работе How Async/Await Really Works in C#. Параграф:

SynchronizationContext and ConfigureAwait.

Там есть пример консольного кода, который далее в том Посте упоминается как our timing sample</p>" data-abbr="our timing sample">our timing sample и который тоже создает дополнительный поток для async lambda функции. Собственно то, что асинхронный метод создает дополнительный поток, и является проблемой, которую автор Поста далее успешно решает, когда подменяет SynchronizationContext для этого исходного примера. Собственно, из того примера Стивена Тоуба вы и должны были сделать вывод, что наличие или отсутствие потока внутри асинхронного метода управляется как раз с помощью SynchronizationContext, который, в некотором смысле, можно считать дополнительным планировщиком (scheduler), который предоставлен нам средой исполнения .NET, и который вы можете переопределить под свои задачи в любой момент.

А вот теперь вооружившись этим знанием о том, что наличием или отсутствием потока
для асинхронных операций управляет контекст синхронизации (SynchronizationContext),
давайте взглянем на пример, который разбирают в статье There is no thread. (Там нет потока):

private async void Button_Click(object sender, RoutedEventArgs e)
{
  byte[] data = ...
  await myDevice.WriteAsync(data, 0, data.Length);
}

Я хочу обратить ваше внимание на название функции Button_Click(), которая однозначно указывает нам какой SynchronizationContext применяется в этом примере, это SynchronizationContext текущего окна (или всего UI). UI SynchronizationContext действительно не создает потоки, он упаковывает асинхронные вызовы в сообщения для единственного UI-потока, ставит их в очередь на исполнение в этом единственном потоке.

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

 Ну вот, надеюсь я ответил на вопрос от @Grave18

Может ли магия async/await сама создавать потоки или это все делается явно тем, кто асинхронные функции пишет?

 Еще раз благодарю за конструктивный вопрос.

Источник: https://habr.com/ru/articles/800495/


Интересные статьи

Интересные статьи

Если вы и собираетесь писать в Твиттере только раз в 11 лет, то лучше сделать это с толком; верный способ – запостить хорошо спланированный мем.Такое заявление генерального директора Meta** Марка Цуке...
Ошибки и нечестность в интернет-рекламе — не редкость. Хитрые фрилансеры, малые агентства с низкоквалифицированными кадрами были, есть и будут. Если KPI по вашему проект...
Я попросил представить нейросеть ChatGPT, которую уже прозвали революционной из-за широкого кругозора и способности давать точные ответы на любые запросы — от сочинения музыки до написания программног...
Несмотря на повсеместное распространение сетей Ethernet, технологии связи на основе DSL не теряют своей актуальности и по сей день. До сих пор DSL можно встретить в сетях последней ми...
Привет, Хабр! Все мы слышали такие слова, как "закаленное стекло", "Gorilla Glass", "химическая обработка стекла". Тем не менее, в большинстве случаев мы и не задумываемся о том, как все эти в...