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

Как запустить Coroutine в другом потоке?

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

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

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

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

Взаимодействие Coroutine и потоков в Unity

Короткий ответ: Coroutine в Unity по умолчанию НЕ запускаются в отдельных потоках. Весь механизм корутин работает в главном потоке (Main Thread) и зависит от игрового цикла Unity. Однако вы можете создать отдельный поток и запустить в нем асинхронные операции, комбинируя их с корутинами для взаимодействия с главным потоком.

Почему Coroutine не работают в отдельных потоках

Coroutine — это механизм кооперативной многозадачности, построенный на основе итераторов C# и интегрированный в игровой цикл Unity. Все этапы выполнения (MoveNext(), возврат управления оператором yield) происходят в главном потоке. Попытка напрямую обращаться к API Unity из другого потока приведет к ошибке:

// НЕПРАВИЛЬНЫЙ ПОДХОД - вызовет ошибку
IEnumerator CoroutineInThread()
{
    // Нельзя вызывать из другого потока!
    transform.position = Vector3.zero; // ОШИБКА: "UnityException: get_transform can only be called from the main thread"
    yield return null;
}

Практические стратегии запуска фоновых операций

1. Thread + Main Thread Dispatch

Создаем отдельный поток для тяжелых вычислений и возвращаем результат в главный поток:

using System.Threading;
using UnityEngine;

public class ThreadedCoroutineExample : MonoBehaviour
{
    private Thread _backgroundThread;
    private bool _isProcessing = false;
    private System.Action _mainThreadCallback;
    
    public void StartBackgroundOperation()
    {
        if (_isProcessing) return;
        
        _isProcessing = true;
        _backgroundThread = new Thread(PerformBackgroundTask);
        _backgroundThread.Start();
    }
    
    private void PerformBackgroundTask()
    {
        // Тяжелые вычисления в отдельном потоке
        System.Threading.Thread.Sleep(2000);
        int result = ComplexCalculation();
        
        // Планируем вызов в главном потоке
        _mainThreadCallback = () => 
        {
            Debug.Log($"Result from thread: {result}");
            _isProcessing = false;
        };
    }
    
    private void Update()
    {
        // Выполняем колбэк в главном потоке
        if (_mainThreadCallback != null)
        {
            _mainThreadCallback();
            _mainThreadCallback = null;
        }
    }
    
    private int ComplexCalculation() { return 42; }
}

2. Task + UnityMainThreadDispatcher

Современный подход с использованием System.Threading.Tasks:

using System.Threading.Tasks;
using UnityEngine;

public class TaskBasedExample : MonoBehaviour
{
    public async void StartAsyncOperation()
    {
        // Запускаем в пуле потоков
        int result = await Task.Run(() => HeavyCalculation());
        
        // Автоматически возвращаемся в главный поток
        ProcessResultInMainThread(result);
    }
    
    private int HeavyCalculation()
    {
        // Имитация тяжелой операции
        System.Threading.Thread.Sleep(3000);
        return 100;
    }
    
    private void ProcessResultInMainThread(int value)
    {
        // Безопасная работа с Unity API
        gameObject.name = $"Result: {value}";
        StartCoroutine(VisualFeedbackCoroutine(value));
    }
    
    private IEnumerator VisualFeedbackCoroutine(int value)
    {
        // Корутина работает в главном потоке
        Vector3 originalScale = transform.localScale;
        transform.localScale *= 1.5f;
        yield return new WaitForSeconds(0.5f);
        transform.localScale = originalScale;
    }
}

3. Unity Job System + Coroutine

Для высокопроизводительных вычислений используйте Job System:

using Unity.Jobs;
using Unity.Collections;
using UnityEngine;

public class JobWithCoroutine : MonoBehaviour
{
    public void StartJobWithCoroutine()
    {
        StartCoroutine(ProcessWithJobSystem());
    }
    
    private IEnumerator ProcessWithJobSystem()
    {
        NativeArray<float> data = new NativeArray<float>(1000, Allocator.TempJob);
        
        // Создаем джобу для параллельных вычислений
        var job = new CalculationJob { data = data };
        JobHandle handle = job.Schedule(data.Length, 64);
        
        // Ждем завершения джобы
        while (!handle.IsCompleted)
        {
            yield return null; // Продолжаем в главном потоке
        }
        
        handle.Complete();
        
        // Обрабатываем результаты
        float sum = 0;
        for (int i = 0; i < data.Length; i++)
        {
            sum += data[i];
        }
        
        data.Dispose();
        Debug.Log($"Job result: {sum}");
    }
    
    struct CalculationJob : IJobParallelFor
    {
        public NativeArray<float> data;
        
        public void Execute(int index)
        {
            data[index] = Mathf.Sin(index * 0.1f);
        }
    }
}

Ключевые рекомендации

  • Главный поток: Используется для всего, что связано с Unity API (трансформы, физика, рендеринг)
  • Отдельные потоки: Применяйте для CPU-интенсивных операций, вычислений, загрузки данных
  • Синхронизация: Всегда возвращайтесь в главный поток для работы с Unity объектами
  • Job System: Предпочтительнее стандартных потоков для параллельной обработки данных
  • Async/Await: Современный и безопасный способ работы с асинхронностью

Паттерн-решение

public class ThreadedComputation : MonoBehaviour
{
    public IEnumerator ComputeInThread(Action<object> onComplete)
    {
        bool isDone = false;
        object result = null;
        
        System.Threading.ThreadPool.QueueUserWorkItem(_ =>
        {
            // Фоновая работа
            result = ExpensiveComputation();
            isDone = true;
        });
        
        // Ожидание в корутине
        while (!isDone)
        {
            yield return null;
        }
        
        onComplete?.Invoke(result);
    }
}

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