Under Construction

Prometheus Lab

Portfolio & Resources

Back to Blog
Unity15 min15 views

Coroutine, Async, UniTask & Đa luồng trong Unity/C#

Author

Minh Khoa

Author

1. Coroutine trong Unity

Định nghĩa

  • Coroutine là cơ chế giả lập bất đồng bộ của Unity.
  • Nó chạy trên main thread duy nhất của Unity (không phải đa luồng thật).
  • Cho phép “tạm dừng” và “tiếp tục” code qua yield return.

Cách hoạt động

  • Coroutine là state machine do Unity quản lý.
  • Mỗi frame, Unity “tick” coroutine → chạy tiếp từ chỗ yield.

Ví dụ

IEnumerator FadeOut()
{
    for (float t = 1; t > 0; t -= Time.deltaTime)
    {
        spriteRenderer.color = new Color(1,1,1,t);
        yield return null; // tạm dừng tới frame sau
    }
}
StartCoroutine(FadeOut());

Ưu điểm

  • Đơn giản, dễ dùng cho animation, delay, sequence.
  • Tích hợp sẵn vào vòng lặp Unity (frame, physics, WaitForSeconds…).

Nhược điểm

  • Không đa luồng → không giảm tải CPU cho tác vụ nặng.
  • Dễ bị “rò” nếu object bị destroy mà không StopCoroutine.
  • Không return giá trị trực tiếp.

2. Async/Await trong C#

Định nghĩa

  • Từ C# 5.0: async/await cho phép viết code bất đồng bộ mà nhìn như đồng bộ.
  • Async không đồng nghĩa với đa luồng:
    • Có thể chạy trên thread khác (Task.Run).
    • Hoặc chỉ “đợi” I/O (không chặn main thread).

Ví dụ

public async Task LoadDataAsync()
{
    string data = await File.ReadAllTextAsync("save.json");
    Debug.Log(data);
}

Trong Unity

  • Unity hỗ trợ C# async, nhưng không hook sẵn vào main loop → không await được yield return.
  • Vì thế có library hỗ trợ (UniTask).

3. UniTask trong Unity

Định nghĩa

  • UniTask là thư viện (Cysharp) giúp dùng async/await với Unity.
  • Tích hợp await cho yield instruction (WaitForSeconds, AsyncOperation, …).
  • Viết lại hệ thống Task của C# dưới dạng struct để không sinh ra rác (Zero Allocation), tối ưu tuyệt đối cho Unity.
  • Tối ưu lightweight hơn

Ví dụ

using Cysharp.Threading.Tasks;

public async UniTaskVoid Start()
{
    await UniTask.Delay(2000); // đợi 2s
    Debug.Log("Done");

    await SceneManager.LoadSceneAsync("Battle").ToUniTask();
}

Ưu điểm

  • Viết logic bất đồng bộ gọn hơn coroutine.
  • Có thể return kết quả (UniTask<T>).
  • Kết hợp tốt với I/O, tải scene, web request.

Nhược điểm

  • Cần cài package ngoài (UniTask).
  • Async flow phức tạp dễ tạo deadlock nếu không cẩn thận.

4. Đa luồng thực sự trong C#

Các API đa luồng

  1. Thread (System.Threading.Thread)
    • Thấp cấp, kiểm soát trực tiếp thread.
    • Tốn tài nguyên, ít dùng trực tiếp trong game.
  2. ThreadPool
    • Hệ thống quản lý pool thread, tối ưu hơn.
  3. Task (TPL – Task Parallel Library)
    • Cao cấp, dễ quản lý.
    • Task.Run(() => { … }) để chạy tác vụ nặng ở background.
  4. Parallel.For / PLINQ
    • Xử lý song song trên nhiều CPU core.
  5. async/await
    • Kết hợp với Task để viết đa luồng bất đồng bộ gọn gàng.

Ví dụ

// Chạy hàm nặng ở background
public async Task<int> HeavyCalcAsync()
{
    return await Task.Run(() =>
    {
        int sum = 0;
        for(int i=0;i<10000000;i++) sum += i;
        return sum;
    });
}

Trong Unity

  • Unity API (GameObject, Transform, MonoBehaviour, …) không thread-safe.
  • Rule: chỉ dùng đa luồng cho xử lý dữ liệu thuần (pathfinding, AI calc, file I/O, network…).
  • Kết quả → trả về main thread để update scene.

5. So sánh tổng quan

image.png---

6. Best Practices trong Unity

  • Coroutine: cho gameplay logic nhỏ, animation, delay → dễ đọc, ít bug.
  • UniTask: cho load scene, asset, web request, chờ async → code đẹp, dễ maintain.
  • Task/Thread: cho xử lý nặng (AI, pathfinding, save/load file lớn) → luôn sync kết quả về main thread bằng UnityMainThreadDispatcher hoặc UniTask.SwitchToMainThread().
  • Không bao giờ chạm Unity API từ thread phụ.

7. Kết luận

  • Coroutine: không phải đa luồng → chỉ “chia thời gian” trên main thread.
  • Async/await: công cụ bất đồng bộ trong C#, có thể chạy đa luồng thực sự khi dùng Task.Run.
  • UniTask: cầu nối async với Unity, giúp viết code gọn và hiệu năng tốt.
  • Đa luồng thực sự (Task/Thread): dùng cho việc tính toán nặng, nhưng phải cực kỳ cẩn thận với Unity API.