[UNITY]여러개의 Task 받아서 처리하기(Task.WhenAny/WhenAll)

🎮 Unity에서 비동기 Task 처리하기 – WhenAny, WhenAll 완벽 정리

Unity에서 비동기 로딩을 하다 보면
"여러 개의 에셋을 비동기로 불러오고, 완료된 순서대로 처리하고 싶다!"
혹은
"전부 로딩될 때까지 기다렸다가 시작하고 싶다!"
이런 상황이 자주 생기죠?

이럴 때 아주 유용한 것이 바로 Task.WhenAny()Task.WhenAll() 입니다.


☝️ Task.WhenAny – 가장 먼저 끝나는 작업만 기다리기

개념

Task.WhenAny(params Task[] tasks)는 여러 개의 Task 중 가장 먼저 완료된 하나의 Task가 반환되도록 기다립니다.

Task<int> task1 = SomeAsyncOperation1();
Task<int> task2 = SomeAsyncOperation2();
Task<int> task3 = SomeAsyncOperation3();

Task<Task<int>> whenAnyTask = Task.WhenAny(task1, task2, task3);
Task<int> completedTask = await whenAnyTask;
int result = await completedTask;
  • whenAnyTask는 가장 먼저 끝난 Task<int>를 감싸는 Task의 Task
  • 결과를 얻기 위해선 한 번 더 await이 필요합니다

💡 실전 예제 – 에셋 로딩: 먼저 끝난 것부터 처리

public async void Load(List<string> assetNames)
{
    List<Task<GameObject>> loadTasks = new List<Task<GameObject>>();

    foreach (string name in assetNames)
    {
        loadTasks.Add(LoaderModule.LoadAssetAsync(name));
    }

    while (loadTasks.Any())
    {
        Task<GameObject> finished = await Task.WhenAny(loadTasks);
        loadTasks.Remove(finished);

        GameObject result = await finished;
        if (result != null)
        {
            Debug.Log($"로딩 완료: {result.name}");
        }
    }
}

✅ 설명

  • 에셋 이름 리스트를 순회하며 Task<GameObject> 리스트를 만듭니다
  • WhenAny로 가장 빨리 로딩된 에셋부터 처리
  • 완료된 작업은 Remove()로 제거 → 남은 것 계속 대기

✌️ Task.WhenAll – 모두 끝날 때까지 기다리기

개념

Task.WhenAll(params Task[] tasks)모든 작업이 완료될 때까지 기다립니다.

Task<int> task1 = SomeAsyncOperation1();
Task<int> task2 = SomeAsyncOperation2();
Task<int> task3 = SomeAsyncOperation3();

await Task.WhenAll(task1, task2, task3);

int result1 = await task1;
int result2 = await task2;
int result3 = await task3;
  • Task.WhenAll() 자체는 완료 시점을 기다릴 뿐, 결과를 바로 주진 않습니다
  • 각 Task의 결과는 별도로 await taskX 로 받아야 합니다

🧪 Unity 에셋 로딩에 적용하면?

public async void LoadAll(List<string> assetNames)
{
    var loadTasks = assetNames.Select(name => LoaderModule.LoadAssetAsync(name)).ToList();

    await Task.WhenAll(loadTasks);

    foreach (var task in loadTasks)
    {
        GameObject result = await task;
        if (result != null)
            Debug.Log($"완료된 오브젝트: {result.name}");
    }
}
  • 모든 에셋이 완전히 로딩된 후 처리
  • 타이틀 화면 → 에셋 다 불러온 후 게임 시작 등에 적합

🎯 WhenAny vs WhenAll 요약 비교

항목 Task.WhenAny Task.WhenAll
목적 가장 먼저 끝난 작업 처리 모든 작업 완료 대기
처리 방식 빠른 순서대로 하나씩 전부 끝난 후 한꺼번에
사용 예시 에셋을 빠른 순서로 인스턴스화 모든 리소스가 준비될 때까지 대기
반환값 완료된 하나의 Task 모든 Task의 완료 보장

🚨 주의할 점

  • WhenAny()한 Task만 반환합니다. 리스트에서 제거 후 반복해야 전체를 순서대로 처리할 수 있어요.
  • WhenAll()하나라도 예외가 발생하면 전체 Task 실패로 처리되니 try-catch로 감싸는 것도 고려하세요.

✅ 마무리

Unity에서 Task.WhenAnyTask.WhenAll을 활용하면,
비동기 에셋 로딩을 더 유연하게 제어할 수 있습니다.
복잡한 상황에서도 효율적으로 비동기 흐름을 관리할 수 있는 강력한 도구죠!