Как работает UniTask?
Комментарии (3)
Ответ сгенерирован нейросетью и может содержать ошибки
UniTask: Современная библиотека для асинхронного программирования в Unity
UniTask — это высокопроизводительная библиотека для C#, переосмысливающая асинхронное программирование в Unity, построенная на основе async/await паттерна. Она была создана для устранения недостатков стандартного System.Threading.Tasks.Task в контексте игрового движка, где критичны производительность, отсутствие аллокаций в куче (heap allocations) и глубокая интеграция с циклом жизни Unity.
Ключевые принципы работы UniTask
В отличие от стандартного Task, который аллоцирует память в куче и зависит от SynchronizationContext, UniTask реализует структурный тип значения (value-type struct). Это фундаментальное отличие приносит несколько преимуществ:
- Нулевые аллокации в куче (Zero Heap Allocations): Поскольку UniTask является структурой, её создание и ожидание (
await) не приводит к выделению памяти в управляемой куче (если операция не требует состояния после завершения). Это критически важно для поддержания плавного FPS и минимизации сборок мусора (GC). - Прямая интеграция с циклом обновления Unity: UniTask предоставляет собственные "авайтеры" (awaiters), которые работают с
PlayerLoopUnity. Это позволяет ожидать (await) кадры, фиксированные обновления, конец кадра и другие специфичные для игрового движка события без дополнительных затрат. - Отмена операций через CancellationToken: Поддержка отмены асинхронных операций через
CancellationTokenи собственныйCancellationTokenSource, связанный сGameObject, который автоматически отменяется при уничтожении объекта.
Основные способы использования
-
Ожидание специфичных для Unity событий:
using Cysharp.Threading.Tasks; using UnityEngine; public class Example : MonoBehaviour { async UniTaskVoid Start() { // Ждём один кадр (аналог yield return null) await UniTask.NextFrame(); // Ждём окончания текущего кадра (аналог WaitForEndOfFrame) await UniTask.WaitForEndOfFrame(); // Ждём 1 секунду с игровым временем (scaled time) await UniTask.Delay(1000); // миллисекунды // Ждём 2 секунды с НЕигровым временем (unscaled time, игнорирует Time.timeScale) await UniTask.Delay(2000, ignoreTimeScale: true); // Ждём, пока условие не станет true (проверяется каждый кадр) await UniTask.WaitUntil(() => transform.position.y > 10); } } -
Возврат результата из асинхронного метода:
// UniTask<T> используется для возврата значений, UniTask — для void-подобных операций. public async UniTask<int> LoadDataAsync(string url) { await UniTask.Delay(500); // Имитация загрузки... return 42; } async UniTaskVoid ProcessData() { int result = await LoadDataAsync("http://example.com/data"); Debug.Log($"Data loaded: {result}"); } -
Управление жизненным циклом и отмена:
public async UniTaskVoid DownloadAssetAsync(CancellationToken token) { try { // Метод будет отменён, если token вызовет отмену ДО завершения задержки. await UniTask.Delay(3000, cancellationToken: token); Debug.Log("Download complete!"); } catch (OperationCanceledException) { Debug.Log("Download was cancelled."); } } void Start() { // Создаём CancellationToken, привязанный к жизненному циклу этого GameObject. var cancellationToken = this.GetCancellationTokenOnDestroy(); DownloadAssetAsync(cancellationToken).Forget(); // Forget() для "огне-и-забудь" задач } -
Асинхронные операции над ресурсами Unity:
// Awaitable-версии стандартных операций Unity async UniTaskVoid LoadSceneAsync() { await SceneManager.LoadSceneAsync("Scene2").ToUniTask(); } async UniTask<Texture> LoadTextureAsync(string path) { // ResourceRequest также может быть ожидаемым var resourceRequest = Resources.LoadAsync<Texture>(path); return (Texture)await resourceRequest; }
Важные особенности и лучшие практики
UniTaskVoid: Используется дляasyncметодов, которые не нужно ожидать (аналогичноasync void). Предпочтительнееasync void, так как позволяет корректно ловить исключения и интегрируется с системой отмены UniTask.- Не требуется
StartCoroutine/yield return: Весь код выполняется в одном методе, что делает его линейным и удобочитаемым, в отличие от корутин. Forget(): Метод для запускаUniTaskилиUniTaskVoid, когда результат не нужно ждать. Он автоматически логирует необработанные исключения (по умолчанию в консоль Unity), что безопаснее, чем игнорировать задачу.UniTask.Run: Запуск тяжёлых вычислений на фоновом потоке пула с последующим возвратом результата в основной поток Unity.UniTask.WhenAll/UniTask.WhenAny: АналогиTask.WhenAll/WhenAnyдля параллельного ожидания нескольких UniTask, но без аллокаций в куче.
В итоге, UniTask работает, предлагая высокооптимизированную, безаллокационную модель асинхронности, глубоко встроенную в жизненный цикл Unity. Она заменяет корутины и стандартные Task в большинстве сценариев, предоставляя более чистый, производительный и удобный для отмены код, что делает её де-факто стандартом для современной асинхронной разработки на Unity.