Должен ли я использовать Async & Await вместо Coroutines в Unity?

Aug 18 2020

Стоит ли использовать Async & Await в Unity? Или мне следует продолжать использовать сопрограммы? Если мне нужно использовать Async & Await, как я могу сделать это аналогично сопрограммам?

Ответы

2 Kevin Aug 19 2020 at 01:04

В отличие от ответа Эворлора, вот несколько веских причин использовать сопрограммы:

  • Они уже много лет являются стандартным решением в Unity, и любой, кого вы пригласите в команду, уже должен чувствовать себя комфортно с ними.
  • Они идут в ногу с двигателем. WaitForFixedUpdate()гарантирует, что следующий шаг в Coroutine будет выполнен во время цикла фиксированного обновления. WaitForEndOfFrame()гарантирует, что следующий шаг будет выполнен в цикле обновления. Если вы используете C # async / await, нет никакого способа гарантировать, что это сложно, чтобы код оставался синхронизированным с движком (см. Комментарии).
  • Точно так же WaitForSeconds()учитывается ток Time.timeScale, что невозможно await.
  • Сопрограммы автоматически останавливаются, когда объект, который их запускает, уничтожается или отключается (иногда это нежелательно, и в этом случае вам может потребоваться async / await).

Еще одно примечание: Evorlor предложил это решение для ожидания состояния:

//Wait until condition has been met
while (!ConditionMet())
{
    yield return new WaitForEndOfFrame();
    Debug.Log("Condition has been met.");
}

Это обычное дело, но не решение для ожидания состояния. Предполагаемое решение имеет меньше шаблонов (и, возможно, меньше накладных расходов на ЦП):

yield return new WaitUntil(() => ConditionMet());

Когда мне не следует использовать сопрограммы?

  • Если вы хотите, чтобы функция, подобная сопрограмме, возвращала значение (см. Это сообщение в блоге, рекомендованное DMGregory)
  • Если у вас возникают проблемы с обработкой ошибок для работы с вашей сопрограммой (см. Сообщение в блоге из предыдущего пункта. Обратите внимание, что вы можете поместить блок try-catch в сопрограмму, если этот блок не включает инструкцию "yield") .
  • Если вы профилировали свое приложение и обнаружили, что сопрограммы требуют накладных расходов на производительность, которые вы не можете сэкономить, и это не может быть исправлено путем оптимизации ваших сопрограмм (что маловероятно для большинства игр)
  • Если вам нужно запустить похожую на сопрограмму функцию из обычного класса C #, который не расширяется MonoBehaviour
  • Если вам нужна функция, подобная сопрограмме, чтобы продолжать работать, когда ее GameObject уничтожен или отключен.
  • При работе с библиотекой, которая интенсивно использует async(например, Google Firebase)
  • Когда ваш код должен быть параллельным (многопоточным). Хотя их можно использовать для повышения производительности путем распределения дорогостоящей операции по нескольким кадрам, сопрограммы не являются многопоточными . Если вам нужен многопоточный код для максимальной производительности, используйте потоки C # или систему заданий Unity.
  • Если у вас есть другие веские причины не использовать сопрограммы

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