Как сделать так чтобы UniTask уничтожалась вместе с MonoBehaviour?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Управление жизненным циклом UniTask в MonoBehaviour
Чтобы UniTask корректно уничтожалась вместе с MonoBehaviour, необходимо организовать управление ее жизненным циклом с использованием CancellationToken. Это важнейший механизм, предоставляемый UniTask для контроля асинхронных операций. Вот основные подходы, которые я использую в своей практике.
Использование CancellationToken из MonoBehaviour
Самый простой и надежный способ — это использование встроенного CancellationToken, который автоматически отменяется при уничтожении GameObject или компонента:
using Cysharp.Threading.Tasks;
using UnityEngine;
using System.Threading;
public class TaskLifecycleExample : MonoBehaviour
{
private void Start()
{
// Запускаем асинхронную задачу с привязкой к жизненному циклу MonoBehaviour
RunAsyncTask().Forget();
}
private async UniTaskVoid RunAsyncTask()
{
try
{
// Используем GetCancellationTokenOnDestroy() для автоматической отмены
await UniTask.Delay(1000, cancellationToken: this.GetCancellationTokenOnDestroy());
Debug.Log("Задача выполнена успешно");
}
catch (OperationCanceledException)
{
// Задача была отменена при уничтожении GameObject
Debug.Log("Задача отменена из-за уничтожения объекта");
}
}
}
Создание собственного CancellationTokenSource
Для более сложных сценариев, где нужен дополнительный контроль, можно создать собственный CancellationTokenSource:
public class AdvancedTaskManager : MonoBehaviour
{
private CancellationTokenSource _cancellationTokenSource;
private void Start()
{
_cancellationTokenSource = new CancellationTokenSource();
StartAsyncOperations();
}
private async UniTaskVoid StartAsyncOperations()
{
var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(
_cancellationTokenSource.Token,
this.GetCancellationTokenOnDestroy()
);
await ProcessDataAsync(linkedTokenSource.Token);
}
private async UniTask ProcessDataAsync(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
// Выполняем работу
await UniTask.Yield(token);
}
}
private void OnDestroy()
{
// Явная отмена всех задач при уничтожении
_cancellationTokenSource?.Cancel();
_cancellationTokenSource?.Dispose();
}
}
Ключевые подходы и рекомендации
-
Всегда используйте CancellationToken — никогда не запускайте UniTask без возможности отмены из MonoBehaviour
-
Предпочитайте встроенные методы:
this.GetCancellationTokenOnDestroy()— основной метод для большинства случаевUniTask.WaitUntilDestroyed(this)— альтернативный подход для ожидания
-
Правильная обработка исключений:
private async UniTaskVoid SafeAsyncMethod() { try { await SomeOperation(this.GetCancellationTokenOnDestroy()); } catch (OperationCanceledException) { // Ожидаемое поведение при уничтожении } } -
Для фоновых задач используйте PlayerLoopTiming:
await UniTask.DelayFrame(1, PlayerLoopTiming.Update, this.GetCancellationTokenOnDestroy()); -
Избегайте распространенных ошибок:
- Не сохраняйте ссылки на незавершенные задачи
- Всегда проверяйте
IsCancellationRequestedв длительных циклах - Используйте
UniTaskVoidвместоUniTaskдля fire-and-forget задач
Паттерн с использованием UniTaskTracker (для отладки)
public class TrackedAsyncBehaviour : MonoBehaviour
{
private void Start()
{
RunTrackedTask().Forget();
}
private async UniTaskVoid RunTrackedTask()
{
// Добавляем задачу в трекер для отладки
var task = LongRunningOperation(this.GetCancellationTokenOnDestroy());
UniTaskTracker.TrackActiveTask(task);
await task;
}
}
Основное правило, которое я всегда соблюдаю: каждая асинхронная операция в MonoBehaviour должна иметь четко определенную точку отмены, привязанную к жизненному циклу объекта. Это предотвращает утечки памяти, исключения MissingReferenceException и обеспечивает стабильность приложения при горячей перезагрузке сцен или уничтожении объектов. Использование GetCancellationTokenOnDestroy() в 95% случаев является оптимальным решением, так как оно абстрагирует управление жизненным циклом и минимизирует boilerplate код.