Вызов асинхронных методов внутри цикла

302
26 февраля 2017, 06:57

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

1.

while (true)
{
    var context = listener.GetContext();
    Task.run(() => HandleContext(context));
}

2.

while (true)
{
    var context = await listener.GetContextAsins();
    Task.run(() => HandleContext(context));
}

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

private async void GetContextAsync()
{
    var context = await listener.GetContextAsins();
    Task.run(() => HandleContext(context));
}

И в цикле:

while(true)
    GetContextAsync();

Или, может быть, правильнее это делать так:

while(true)
    listener.GetContextAsync().ContinueWith(...);

Пожалуйста, Поправьте меня, если я не прав, и покажите как делать правильно.

Answer 1

Давайте посмотрим, что вам реально нужно. Вам нужно получить новое обращение (контекст), запустить его асинхронно на длительную обработку, и не дожидаясь окончания обработки ожидать прихода нового контекста, правильно?

ожидание -----> пришёл контекст -> ожидание -----------> пришёл новый контекст -> ожидание --
                     \                                       \
                      \                                       \
                   обрабатываем контекст -------------------------> закончили
                                                                \
                                                           обрабатываем новый контекст ----->

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

А вот третий вариант с async void, судя по всему, неправильный: async void означает, что содержимое метода запустится, и никто не будет дожидаться его результата. То есть итерации цикла будут бежать одна за другой, не дожидаясь даже окончания получения контекста. В результате у вас будет параллельно висеть очень много незавершённых await listener.GetContextAsync();. Вариант с listener.GetContextAsync().ContinueWith(...); точно так же порождает огромное число параллельных задач ожидания получения контекста.

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

ожидание -----> пришёл контекст
                    \
                     \
                   обрабатываем контекст
 ожидание -----> ?
  ожидание -----> ?
   ожидание -----> ?
    ожидание -----> ?
     ожидание -----> ?
      .
       .
        .

Вы видите, что одновременно происходит много ожиданий, это явно не то, что вам нужно.

READ ALSO
Как правильно освободить Image [дубликат]

Как правильно освободить Image [дубликат]

На данный вопрос уже ответили:

267
FormatException при заполнении DataGridView - C#

FormatException при заполнении DataGridView - C#

Прошу помочь с DataGridVievМне нужно, чтобы была таблица в 2 столбца с ранее указанным количеством строк (gg)

471
Заменить спрайт у дочернего объекта в Unity C#

Заменить спрайт у дочернего объекта в Unity C#

Всем приветЕсть задача, сделать кнопку с картинкой и чтобы при наведении на эту кнопку менялась картинка

478