2

Где и как будут выполнены методы DoCPUBoundOperation и DoIOBoundOperation? На сколько мне известно для IO Bound операций не нужны потоки и они выполняются в "пространстве ядра"(kernel space). В свою очередь CPU Bound операция выполняются "на CPU", поэтому требуют собственный поток. Опишите пожалуйста порядок как будет происходит процесс выполнения кода ниже.

static async Task Main(string[] args)
{
    var cpuResult = await DoCPUBoundOperation();
    var ioResult = await DoIOBoundOperation();
Console.WriteLine($"cpuResult = {cpuResult}, ioResult = {ioResult}");
Console.ReadKey();

}

public static async Task<string> DoIOBoundOperation() { var client = new WebClient(); var uri = new Uri("http://someAdress");

return await client.DownloadStringTaskAsync(uri);

}

public static Task<int> DoCPUBoundOperation() { var sum = 0; for (int i = 0; i <= 10; i++) { sum += i; }

Thread.Sleep(10000);
return Task.FromResult(sum);

}

Ряд вопросов которые нуждаются в отдельном внимании

  1. После попадания внутрь метода DoIOBoundOperation, в каком потоке будет выполняться кусок кода
 var client = new WebClient();
 var uri = new Uri("http://someAdress");
  1. В месте вызова

    var ioResult = await DoIOBoundOperation();
    

для выполнения DoIOBoundOperation метода будет выделен отдельный поток? И если да, то поток возьмется из пула потоков?

4per
  • 2,696
Dori
  • 39
  • 6
  • IO-операции выполняются на IOCP - портах завершения ввода-вывода. То есть частично их берёт на себя "железо". https://ru.stackoverflow.com/a/663039/184217 – Alexander Petrov Dec 11 '20 at 08:08

1 Answers1

5

1)После поподания внутрь метода DoIOBoundOperation, в каком потоке будет выполняться кусок кода

В методе

   public static async Task<string> DoIOBoundOperation()
   {
       var client = new WebClient();
       var uri = new Uri("http://someAdress");
   return await client.DownloadStringTaskAsync(uri);

}

Асинзронная только последняя операция. То есть всё, что до этой операции выполняется синхронно, как в обычном методе.

  1. В месте вызова var ioResult = await DoIOBoundOperation(); для выполнения DoIOBoundOperation метода будет выделен отдельный поток?И если да, то поток возьмется из пула потоков?

Асинхронный != многопоточный. Поток может быть создан, а может и нк быть создан. Для IO операций потоки не создаются, то есть для самой операции поток не нужен. Но вот рассмотрим следующий код

{код 1}
await IO operation
{код 2 - называется код-продолжение}

так вот код до асинхронной операции и код после асинзроннорй операции могут выполняться в одном и том же потоке или в разных потоках.

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

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

Немного инфы тут.

Что касается DoCPUBoundOperation- то этот код не является асинхронным. Это обычный синхронный код.

tym32167
  • 32,857
  • Прошу прощения, но все равно не понимаю момент. var ioResult = await DoIOBoundOperation(); Вот здесь он зайдет в метод DoIOBoundOperation, этот метод точно не успеет выполниться, поэтому метод Main должен будет вернуть управление и ожидать пока закончится метод DoIOBoundOperation (на сколько мне известно он не сможет никому не вернуть, но как говорится он не виноват, что некому возвращать), но в то же время метод DoIOBoundOperation должен же где-то выполняться(Мне вот как раз непонятен момент где и кем). Возможно, я конечно что-то не понял. Заранее спасибо – Dori Dec 11 '20 at 07:10
  • @Dori Асинхронное программирование. При чем тут управление? Метод просто завершается, а в Task втыкается некий колбэк, который является продолжением выполняемого метода. То есть пока идет ожидание I/O операции, код не выполняется совсем. Как только операция завершилась, Task помечается как выполненный, и запускается колбэк-продолжение (при наличии контекста синхронизации - с его восстановлением, если явно не указано обратное с помощью .ConfigureAwait(false)). – aepot Dec 11 '20 at 08:27
  • @Dori Как я понял, вы пытаетесь понять, как работает этот с виду линейный код. А он не линейный на самом деле, это машина состояний (пример машины состояний). То есть, возврат произойдет в этот же метод, только уже в новом состоянии. Грубо говоря, сколько у вас await раз встречается в методе, столько раз он и будет перезапущен, только каждый раз в новом состоянии. Попросту говоря состояние указывает на то место, с которого надо продолжить выполнение кода. – aepot Dec 11 '20 at 08:30
  • @Dori вы все ещё воспринимаете код как синзронный, хотя он не синхронный. Всё, что после await оператора можно рассматривать как callback. Условано говоря, такой код отработает, когда произойдет событие, что Task под await завершился. Подробнее про это можно почитать в книге Джона Скита C# In Depth - там прямо все разжевано как это все работает. – tym32167 Dec 11 '20 at 11:59
  • 1
    @aepot Спасибо за полезные ссылки, очень информативный материал. – Dori Dec 13 '20 at 09:34
  • 1
    @tym32167Спасибо за книжку, сейчас пойду читать, может после станет окончательно ясно) – Dori Dec 13 '20 at 09:34