Unity••15 min•15 views••
Coroutine, Async, UniTask & Đa luồng trong Unity/C#
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/awaitcho 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
Taskcủ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
- 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.
- ThreadPool
- Hệ thống quản lý pool thread, tối ưu hơn.
- Task (TPL – Task Parallel Library)
- Cao cấp, dễ quản lý.
Task.Run(() => { … })để chạy tác vụ nặng ở background.
- Parallel.For / PLINQ
- Xử lý song song trên nhiều CPU core.
- 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
---
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
UnityMainThreadDispatcherhoặcUniTask.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.