Может ли длинная цепочка асинхронного ожидания вызвать большое потребление памяти? (Теоретически) - PullRequest
5 голосов
/ 21 апреля 2019

Глядя на этот код:

public async Task<T> ConsumeAsync()
    {
          await a();
          await b();
          await c();
          await d();
          //..
    }

Допустим, a,b,c,d также имеет вложенные асинхронные ожидания (и т. Д.)

Async / await POV - для каждого await сохраняется конечный автомат.

Вопрос (теоретический):

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

Ответы [ 3 ]

8 голосов
/ 21 апреля 2019

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

Очень маловероятно.Каждый конечный автомат будет занимать несколько десятков байтов снаружи.

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

Но это не совсем новая или другая форма любого другого типа ресурса.

6 голосов
/ 21 апреля 2019

Асинхронное / ожидающее POV - для каждого ожидающего события сохраняется конечный автомат.

Не соответствует действительности.Компилятор генерирует конечный автомат для каждого метода async.Локальные данные в методе поднимаются в поля конечного автомата.Тело метода (в основном) разбито на оператор switch, где каждый case соответствует части метода между await операторами.int используется для отслеживания того, какой бит метода был выполнен (т. Е. Какой case должен быть выполнен следующим).

Ваши методы a(), b() и т. Д. Могут иметьих собственные конечные автоматы, или они могли бы не (в зависимости от того, помечены ли они async или нет).Даже если это произойдет, в вашем примере за один раз будет создан только один из этих конечных автоматов.

SharpLab - отличный ресурс для изучения этого материала. Пример .

3 голосов
/ 21 апреля 2019

Существует дополнительная стоимость, но она относительно мала.

Дополнительные затраты по сравнению с обычной функцией:

  • Класс для конечного автомата
  • экземплярэтого класса
  • один тип int для этапа выполнения
  • экземпляр AsyncTaskMethodBuilder

Кроме того, локальные переменные функции будут преобразованы в поля конечного автомата.Это перемещает некоторую память из стека в кучу.

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

Существуют некоторые онлайн-инструментычтобы сделать это также (как sharplab.io) См. результаты декомпиляции тривиальной асинхронной функции

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...