← Назад к вопросам

Будет ли игра продолжать свою работу если в Coroutine использовать Sleep?

2.0 Middle🔥 151 комментариев
#Unity Core#Асинхронность и многопоточность

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Нет, игра не будет продолжать свою работу в нормальном режиме, если в Coroutine использовать Thread.Sleep(). Это приведет к "замораживанию" всего основного потока игры (main thread), на котором работает Unity, включая все корутины, обновление сцен, физику и рендеринг.

Почему так происходит?

Корутины в Unity — это не потоки. Они выполняются на основном потоке игры. Их "параллельность" — это иллюзия, созданная системой yield-инструкций, которые приостанавливают выполнение конкретной корутины до наступления определенного события, не блокируя при этом весь поток.

Ключевое отличие: Yield vs Sleep

  • yield return new WaitForSeconds(2f); — Корректная инструкция для корутины. Она говорит движку: "Приостанови эту корутину на 2 секунды, но продолжай выполнять всё остальное в игре (другие скрипты, физику, рендеринг)".
  • System.Threading.Thread.Sleep(2000); — Инструкция уровня операционной системы. Она говорит: "Заблокируй весь текущий поток (то есть основной поток Unity) на 2 секунды". На это время игра полностью зависнет.

Практический пример и сравнение

Давайте рассмотрим разницу в поведении на примере.

using UnityEngine;
using System.Collections;
using System.Threading; // Необходимо для Thread.Sleep

public class SleepExample : MonoBehaviour
{
    public Transform objectToMove;
    private Vector3 startPosition;

    void Start()
    {
        startPosition = objectToMove.position;
        // Запускаем две корутины для наглядности
        StartCoroutine(CoroutineWithYield());
        StartCoroutine(CoroutineWithThreadSleep());
    }

    void Update()
    {
        // Эта функция должна вызываться каждый кадр.
        // При использовании Thread.Sleep в корутине она БУДЕТ БЛОКИРОВАНА.
        objectToMove.Rotate(0, 90 * Time.deltaTime, 0);
    }

    IEnumerator CoroutineWithYield()
    {
        Debug.Log("Yield Coroutine: Начало. Объект будет двигаться плавно.");
        float duration = 3f;
        float elapsed = 0f;

        while (elapsed < duration)
        {
            // Плавная интерполяция позиции КАЖДЫЙ КАДР
            objectToMove.position = Vector3.Lerp(startPosition, startPosition + Vector3.right * 3, elapsed / duration);
            elapsed += Time.deltaTime;
            yield return null; // Ждем СЛЕДУЮЩЕГО КАДРА, не блокируя поток
        }
        Debug.Log("Yield Coroutine: Завершено.");
    }

    IEnumerator CoroutineWithThreadSleep()
    {
        Debug.Log("Sleep Coroutine: Начало. Игра ЗАВИСНЕТ на 3 секунды.");
        // ВНИМАНИЕ: ЭТО НЕПРАВИЛЬНЫЙ КОД ДЛЯ UNITY!
        Thread.Sleep(3000); // Блокирует ВЕСЬ основной поток на 3000 мс
        Debug.Log("Sleep Coroutine: Завершено. Игра 'отвиснет'.");
        yield break;
    }
}

Что произойдет при запуске этого кода:

  1. Корутина CoroutineWithYield:
    *   Объект начнет плавно двигаться и вращаться в `Update`.
    *   Логика движения внутри корутины выполняется каждый кадр благодаря `yield return null`.
    *   Игра остается полностью отзывчивой.

  1. Корутина CoroutineWithThreadSleep:
    *   Как только выполнение дойдет до `Thread.Sleep(3000)`, **основной поток Unity будет заблокирован**.
    *   На 3 секунды полностью остановится:
        *   Цикл рендеринга (изображение зафризится).
        *   Цикл обновления (`Update`, `FixedUpdate`).
        *   Обработка физики.
        *   **Все другие корутины**, включая `CoroutineWithYield`.
        *   Отклик на ввод пользователя.
    *   Через 3 секунды игра "оживет", как будто ничего не произошло, но игрок увидит резкий застывший кадр.

Итог и правильные альтернативы

Никогда не используйте Thread.Sleep() внутри корутины или любого другого метода, выполняющегося на основном потоке Unity.

Для ожидания в корутинах всегда используйте yield-инструкции:

  • yield return new WaitForSeconds(t); — Ожидание в секундах игрового времени (зависит от Time.timeScale).
  • yield return new WaitForSecondsRealtime(t); — Ожидание в реальных секундах (не зависит от Time.timeScale).
  • yield return null; или yield return 0; — Ожидание следующего кадра.
  • yield return new WaitForEndOfFrame(); — Ожидание конца рендеринга кадра.
  • yield return new WaitUntil(() => condition); — Ожидание выполнения условия.
  • yield return new WaitWhile(() => condition); — Ожидание, пока условие истинно.

Если вам нужна истинная многопоточность для тяжелых вычислений (например, генерация мира, сложные математические расчеты), используйте System.Threading.Tasks.Task, ThreadPool или класс Thread, но тогда вы должны будете возвращать результат в основной поток с помощью, например, UnityEngine.Dispatchers или выполняя финальную часть работы в Update() после установки флага из фонового потока.

Будет ли игра продолжать свою работу если в Coroutine использовать Sleep? | PrepBro