UE5 비동기 & 태스크 시스템
Unreal Engine은 게임 스레드 외에 다수의 워커 스레드를 운영합니다. 무거운 연산을 백그라운드 스레드로 분산하면 게임 스레드의 프레임 시간을 절약할 수 있습니다. UE5에서는 TaskGraph, AsyncTask, UE::Tasks 세 가지 주요 API를 제공합니다.
UE 스레드 구조
Section titled “UE 스레드 구조”| 스레드 | 역할 |
|---|---|
| Game Thread | Actor Tick, 입력 처리, 블루프린트 실행 |
| Render Thread | 렌더 커맨드 생성 |
| RHI Thread | GPU 커맨드 제출 |
| Worker Threads | TaskGraph 작업 실행 풀 |
UObject·AActor·UWorld에 대한 직접 접근은 반드시 Game Thread에서만 허용됩니다.
AsyncTask — 가장 간단한 스레드 전환
Section titled “AsyncTask — 가장 간단한 스레드 전환”// 백그라운드 스레드에서 실행AsyncTask(ENamedThreads::AnyBackgroundThreadNormalTask, [](){ // 무거운 연산 (파일 I/O, 경로 탐색 등) TArray<FString> Results = LoadHeavyData();
// 결과를 Game Thread로 전달 AsyncTask(ENamedThreads::GameThread, [Results = MoveTemp(Results)]() { // Game Thread에서 Actor 업데이트 GEngine->AddOnScreenDebugMessage(-1, 3.f, FColor::Green, FString::Printf(TEXT("로드 완료: %d개"), Results.Num())); });});ENamedThreads 주요 값
Section titled “ENamedThreads 주요 값”| 값 | 스레드 |
|---|---|
GameThread | 메인 게임 스레드 |
AnyBackgroundThreadNormalTask | 워커 스레드 (일반 우선순위) |
AnyBackgroundHiPriTask | 워커 스레드 (높은 우선순위) |
AnyThread | 여유 스레드 자동 선택 |
TaskGraph — 의존성 기반 태스크
Section titled “TaskGraph — 의존성 기반 태스크”복수의 태스크 간 의존성(선후 관계)을 설정할 때 사용합니다.
#include "Async/TaskGraphInterfaces.h"
// 태스크 클래스 정의class FMyTask{public: static const TCHAR* GetTaskName() { return TEXT("MyHeavyTask"); }
FORCEINLINE static TStatId GetStatId() { RETURN_QUICK_DECLARE_CYCLE_STAT(FMyTask, STATGROUP_TaskGraphTasks); }
static ENamedThreads::Type GetDesiredThread() { return ENamedThreads::AnyBackgroundThreadNormalTask; }
static ESubsequentsMode::Type GetSubsequentsMode() { return ESubsequentsMode::TrackSubsequents; }
void DoTask(ENamedThreads::Type CurrentThread, const FGraphEventRef& MyCompletionGraphEvent) { // 실제 작업 HeavyComputation(); }};
// 태스크 디스패치 및 완료 대기FGraphEventRef Task = TGraphTask<FMyTask>::CreateTask() .ConstructAndDispatchWhenReady();
// 의존 태스크 — Task 완료 후 실행FGraphEventArray Prerequisites = { Task };FGraphEventRef DependentTask = TGraphTask<FFollowUpTask>::CreateTask(&Prerequisites) .ConstructAndDispatchWhenReady();UE::Tasks — UE 5.1+ 권장 API
Section titled “UE::Tasks — UE 5.1+ 권장 API”UE 5.1부터 도입된 경량 태스크 API로, TaskGraph보다 간결합니다.
#include "Tasks/Task.h"
using namespace UE::Tasks;
void AMyActor::RunAsyncWork(){ // 백그라운드 태스크 실행 FTask Task = Launch(TEXT("HeavyWork"), []() -> int32 { return PerformHeavyCalculation(); });
// 태스크 완료 후 콜백 (파이프라인) Launch(TEXT("ProcessResult"), [Task]() { int32 Result = Task.GetResult(); // 주의: 여기도 워커 스레드 — UObject 접근 불가 }, Prerequisites(Task));}태스크 완료 대기 (Blocking)
Section titled “태스크 완료 대기 (Blocking)”FTask Task = Launch(TEXT("Work"), []() { HeavyWork(); });
// Game Thread에서 블로킹 대기 — 프레임 드롭 주의Task.Wait();
// 또는 완료 여부만 확인if (Task.IsCompleted()) { ... }Async / Await 스타일 (Coroutine)
Section titled “Async / Await 스타일 (Coroutine)”UE5는 C++ 코루틴을 지원하지 않지만, 플러그인 UE5Coro 또는 PromiseAPI를 활용하면 유사한 패턴을 구현할 수 있습니다. 공식적으로는 Latent Action이 대안입니다.
// Latent Action — Blueprint에서도 사용 가능한 비동기 노드UFUNCTION(BlueprintCallable, meta=(Latent, LatentInfo="LatentInfo"))void LoadDataAsync(FLatentActionInfo LatentInfo, FString Path, FString& OutResult){ if (UWorld* World = GetWorld()) { FLatentActionManager& Manager = World->GetLatentActionManager(); if (!Manager.FindExistingAction<FMyLatentAction>( LatentInfo.CallbackTarget, LatentInfo.UUID)) { Manager.AddNewAction(LatentInfo.CallbackTarget, LatentInfo.UUID, new FMyLatentAction(Path, OutResult, LatentInfo)); } }}스레드 안전 규칙 요약
Section titled “스레드 안전 규칙 요약”// ✅ 워커 스레드에서 안전한 작업- 순수 수학 연산- TArray / TMap 로컬 복사본 조작- FString 생성·파싱- 파일 I/O (IPlatformFile)
// ❌ 워커 스레드에서 금지- UObject 생성·접근 (NewObject, GetWorld 등)- Actor Spawn / Destroy- UE_LOG (스레드 안전하지 않은 버전)- Slate / UMG UI 접근