PART 8 - 강의 2/6

AsyncTask 활용

간편한 비동기 작업 실행과 결과 처리

01

AsyncTask 기본

가장 간단한 비동기 실행 방법

AsyncTask는 지정한 스레드에서 람다 함수를 실행하는 가장 간단한 방법입니다. 헤더 하나만 포함하면 바로 사용할 수 있습니다.

C++
#include "Async/Async.h" // 백그라운드 스레드에서 작업 실행 AsyncTask(ENamedThreads::AnyBackgroundThreadNormalTask, [this]() { // 무거운 계산 수행 (백그라운드 스레드) int32 Result = PerformHeavyCalculation(); // Game Thread로 결과 전달 AsyncTask(ENamedThreads::GameThread, [this, Result]() { // UI 업데이트 등 Game Thread 작업 OnCalculationComplete(Result); }); });

Named Threads

GameThread
RenderThread (Actual)
RHIThread
AnyThread
AnyBackgroundThreadNormalTask
AnyBackgroundHiPriTask
02

Async 함수와 TFuture

결과를 반환하는 비동기 작업

C++
#include "Async/Async.h" // 결과를 반환하는 비동기 작업 TFuture<int32> Future = Async(EAsyncExecution::ThreadPool, []() { // 무거운 계산 return ExpensiveCalculation(); }); // 방법 1: 결과 대기 (블로킹) int32 Result = Future.Get(); // 완료될 때까지 대기 // 방법 2: 완료 여부 확인 if (Future.IsReady()) { int32 Result = Future.Get(); } // 방법 3: 완료 콜백 (논블로킹, 권장) Future.Then([](int32 Result) { // 결과 처리 (백그라운드 스레드에서 실행!) UE_LOG(LogTemp, Log, TEXT("Result: %d"), Result); });

EAsyncExecution 옵션

C++
// 스레드 풀 사용 (기본, 권장) Async(EAsyncExecution::ThreadPool, []() { ... }); // Task Graph 사용 Async(EAsyncExecution::TaskGraph, []() { ... }); // 전용 스레드 생성 (비권장, 오버헤드 큼) Async(EAsyncExecution::Thread, []() { ... }); // Large Thread Pool (대용량 작업) Async(EAsyncExecution::LargeThreadPool, []() { ... });
03

실전 패턴

자주 사용하는 비동기 패턴

Fire and Forget

결과가 필요 없는 백그라운드 작업

AsyncTask(ENamedThreads::AnyBackgroundThreadNormalTask, []() { // 로깅, 통계 전송 등 SendAnalytics(); });
Compute and Return

계산 후 Game Thread에서 결과 처리

TFuture<FData> Future = Async(EAsyncExecution::ThreadPool, []() { return ComputeData(); }); Future.Then([this](FData Data) { AsyncTask(ENamedThreads::GameThread, [this, Data]() { ProcessData(Data); }); });

파일 I/O 예제

C++
void UMySubsystem::LoadDataAsync(const FString& FilePath) { // 경로를 값으로 캡처 Async(EAsyncExecution::ThreadPool, [FilePath, this]() { FString FileContent; // 파일 읽기 (백그라운드) if (FFileHelper::LoadFileToString(FileContent, *FilePath)) { // 파싱 (백그라운드) TSharedPtr<FJsonObject> JsonObject; TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(FileContent); if (FJsonSerializer::Deserialize(Reader, JsonObject)) { // Game Thread에서 결과 처리 AsyncTask(ENamedThreads::GameThread, [this, JsonObject]() { ProcessJsonData(JsonObject); }); } } }); }
04

취소 가능한 비동기 작업

작업 중단 처리

C++
UCLASS() class UMyAsyncLoader : public UObject { GENERATED_BODY() public: void StartAsyncWork() { // 취소 플래그 초기화 bCancelRequested = false; Async(EAsyncExecution::ThreadPool, [this]() { for (int32 i = 0; i < 1000; i++) { // 취소 확인 if (bCancelRequested) { UE_LOG(LogTemp, Warning, TEXT("Work cancelled")); return; } // 작업 수행 DoWorkUnit(i); } // 완료 콜백 AsyncTask(ENamedThreads::GameThread, [this]() { OnWorkComplete(); }); }); } void CancelWork() { bCancelRequested = true; } private: // Atomic으로 스레드 안전하게 FThreadSafeBool bCancelRequested; };
취소 패턴

FThreadSafeBool을 사용하면 락 없이 안전하게 취소 플래그를 확인할 수 있습니다. 긴 작업에서는 주기적으로 취소 여부를 확인하세요.

05

주의사항

흔한 실수 피하기

주의사항
  • UObject 접근 금지 - 백그라운드 스레드에서 UObject 접근 시 크래시
  • 참조 캡처 주의 - 지역 변수 참조 캡처 시 댕글링 위험
  • this 캡처 주의 - 객체 파괴 후 콜백 실행 시 크래시
  • 너무 많은 작업 - 스레드 풀 고갈 주의
C++ - 안전한 this 캡처
// 약한 참조로 안전하게 처리 TWeakObjectPtr<UMyObject> WeakThis(this); Async(EAsyncExecution::ThreadPool, [WeakThis]() { // 백그라운드 작업 FData Result = ComputeData(); AsyncTask(ENamedThreads::GameThread, [WeakThis, Result]() { // 객체가 아직 유효한지 확인 if (UMyObject* This = WeakThis.Get()) { This->ProcessResult(Result); } }); });
SUMMARY

핵심 요약

핵심 포인트
  • AsyncTask - 지정 스레드에서 람다 실행, Fire-and-Forget
  • Async + TFuture - 결과를 반환하는 비동기 작업
  • Future.Then() - 논블로킹 결과 처리
  • EAsyncExecution::ThreadPool - 기본 권장 옵션
  • TWeakObjectPtr - 안전한 UObject 참조
  • FThreadSafeBool - 취소 플래그용
PRACTICE

도전 과제

배운 내용을 직접 실습해보세요

실습 1: AsyncTask로 비동기 작업 실행

AsyncTask(ENamedThreads::AnyBackgroundThreadNormalTask, Lambda)를 사용하여 RPG의 대규모 데이터 계산(경로 탐색, 시야 확인)을 백그라운드에서 수행하세요.

실습 2: FAsyncTask로 재사용 가능한 태스크

FNonAbandonableTask를 상속하여 RPG 월드 청크 생성 태스크를 구현하세요. FAsyncTask로 래핑하고, StartBackgroundTask()로 시작, IsDone()으로 완료 체크하세요.

심화 과제: Game Thread로 결과 전달

백그라운드 태스크 완료 후 AsyncTask(ENamedThreads::GameThread, Callback)를 사용하여 결과를 Game Thread에서 안전하게 처리하세요. UObject 접근은 반드시 Game Thread에서 하도록 보장하세요.