Unity unitask는 unity에서 비동기 프로그래밍을 더효율적으로 처리하기 위해 설계된 경량의 비동기/대기 프레임워크 입니다. 기존 C# 'async'와 'await' 패턴을 활용하지만, unity의 메인 스레드와 통합되도록 최적화 되어있는 unity 전용 라이브러리라고 생각하면 됩니다.
Unitask란?
Unitask는 unity용 비동기 프로그래밍 라이브러리로, unity의 'Coroutine'이나 기존의 'async/await'을 대체할 수 있는 효율적인 방법을 제공합니다. 게임개발에서 많은 비동기 작업들이 메인 게임 루프와 밀접하게 연결되어있을 때 유용하게 사용할 수 있습니다.
Unitask는 성능 최적화를 제공하며 메모리 할당을 최소화하여 가비지 컬렉션을 줄입니다.
Unitask의 주요 특징
가비지 컬렉션의 최소화: Unitask는 가비지 컬렉션(GC)의 압력을 줄이기 위해 특별히 설계되었습니다. 게임의 프레임률과 성능에 직접적으로 영향을 미칩니다.
Unity의 메인스레드와의 통합: Unitask는 unity의 메인 스레드와의 통합을 용이하게 합니다. 이는 unity의 생명주기 이벤트와의 호환성을 의미합니다.
경량화: Unitask는 경량화되어 있으며, 추가적인 성능 오버헤드 없이 비동기 작업을 수행할 수있습니다.
C# Async/Await
C#에서 async/await 메서드는 "비동기 함수"로 불립니다. 기존에 존재하던 Task의 장점을 살려 개발자가 좀 더 쉽게 비동기 작업을 수행할 수 있도록 만든 것이 async/await입니다.
await 연산자는 피연산자가 나타내는 비동기 작업이 완료될 때 까지 수행을 중지합니다. 그리고 await 연산자를 포함한 메서드에 async를 붙여 컴파일러가 해당 함수가 비동기 함수임을 알 수 있게 해줍니다.
async/await 키워드는 "Task"를 사용하므로 여러 스레드를 활용하는게 특징입니다.
Async/await is a syntactic sugar built on top of Promises to make it easier to work with asynchronous code
중요한 특징은 async/await은 syntax sugar라는 점입니다. 즉, async/await 키워드 없이도 이를 구현할 수 있습니다!
다음 코드가 있다고 가정합니다.
static class DBExecutor
{
static public async Task<string> ExecuteDB(string spName, long accountId)
{
string result;
Console.WriteLine($"DB 연결 시작");
await Task.Delay(2000);
Console.WriteLine($"{spName} 호출 시작.");
await Task.Delay(2000);
Console.WriteLine($"{spName} 호출 종료.");
result = "장형이";
return result;
}
}
이를 reverse enginerring을 통해 decompile 시키면,
internal static class DBExecutor
{
public static Task<string> ExecuteDB(string spName, long accountId)
{
ExecuteDBState stateMachine = new ExecuteDBState();
stateMachine.builder = AsyncTaskMethodBuilder<string>.Create();
stateMachine.spName = spName;
stateMachine.accountId = accountId;
stateMachine.state = -1;
stateMachine.builder.Start(ref stateMachine);
return stateMachine.builder.Task;
}
private sealed class ExecuteDBState : IAsyncStateMachine
{
public int state;
public AsyncTaskMethodBuilder<string> builder;
public string spName;
public long accountId;
private string result;
private TaskAwaiter awaiter;
void IAsyncStateMachine.MoveNext()
{
int currentState = this.state;
string result;
try
{
TaskAwaiter awaiter1;
int nextState;
TaskAwaiter awaiter2;
switch (currentState)
{
case 0:
awaiter1 = this.awaiter;
this.awaiter = new TaskAwaiter();
this.state = nextState = -1;
break;
case 1:
awaiter2 = this.awaiter;
this.awaiter = new TaskAwaiter();
this.state = nextState = -1;
goto EndProc;
default:
Console.WriteLine("DB 연결 시작");
awaiter1 = Task.Delay(2000).GetAwaiter();
if (!awaiter1.IsCompleted)
{
this.state = nextState = 0;
this.awaiter = awaiter1;
ExecuteDBState stateMachine = this;
this.builder.AwaitUnsafeOnCompleted(ref awaiter1, ref stateMachine);
return;
}
break;
}
awaiter1.GetResult();
Console.WriteLine(this.spName + " 호출 시작.");
awaiter2 = Task.Delay(2000).GetAwaiter();
if (!awaiter2.IsCompleted)
{
this.state = nextState = 1;
this.awaiter = awaiter2;
ExecuteDBState stateMachine = this;
this.builder.AwaitUnsafeOnCompleted(ref awaiter2, ref stateMachine);
return;
}
EndProc:
awaiter2.GetResult();
Console.WriteLine(this.spName + " 호출 종료.");
this.result = "장형이";
result = this.result;
}
catch (Exception ex)
{
this.state = -2;
this.result = (string)null;
this.builder.SetException(ex);
return;
}
this.state = -2;
this.result = (string)null;
this.builder.SetResult(result);
}
}
}
와 같이 변환됩니다. 즉, 컴파일러가 async가 붙은 함수를 class로 구현된 finite state machine으로 변환합니다.
재밌는 건, 구현된 클래스의 메서드를 실행하는 주체가 컴파일러라는 점입니다. 작성한 ExecuteDB async함수의 실행부가 완전히 달라진 것을 확인할 수 있습니다.
Unitask
Task는 C#의 "Awaitable pattern"에 대한 구현체이고, Unitask 또한 C#의 "Awaitable pattern"의 구현체입니다. 따라서, Task와 unitask는 async/await 용법이 같을 뿐 전혀 다르게 동작합니다. 즉, C#에서는 비동기 메커니즘이 어떻게 동작할지에 대한 구현체를 직접 구현할 수 있습니다. async/await은 단순히 async 함수를 await을 분기로하는 state machine으로 변경해줍니다.
다음 시간에는 직접 custom awaitable pattern을 구현하는 LEapsTask를 만들어 더 깊게 이해하는시간을 가져봅니다.
Unitask 정리
Unity unitask는 unity에서 비동기 프로그래밍을 더효율적으로 처리하기 위해 설계된 경량의 비동기/대기 프레임워크 입니다. 기존 C# 'async'와 'await' 패턴을 활용하지만, unity의 메인 스레드와 통합되도록 최적화 되어있는 unity 전용 라이브러리라고 생각하면 됩니다.
Unitask란?
Unitask는 unity용 비동기 프로그래밍 라이브러리로, unity의 'Coroutine'이나 기존의 'async/await'을 대체할 수 있는 효율적인 방법을 제공합니다. 게임개발에서 많은 비동기 작업들이 메인 게임 루프와 밀접하게 연결되어있을 때 유용하게 사용할 수 있습니다.
Unitask는 성능 최적화를 제공하며 메모리 할당을 최소화하여 가비지 컬렉션을 줄입니다.
Unitask의 주요 특징
가비지 컬렉션의 최소화: Unitask는 가비지 컬렉션(GC)의 압력을 줄이기 위해 특별히 설계되었습니다. 게임의 프레임률과 성능에 직접적으로 영향을 미칩니다.
Unity의 메인스레드와의 통합: Unitask는 unity의 메인 스레드와의 통합을 용이하게 합니다. 이는 unity의 생명주기 이벤트와의 호환성을 의미합니다.
경량화: Unitask는 경량화되어 있으며, 추가적인 성능 오버헤드 없이 비동기 작업을 수행할 수있습니다.
C# Async/Await
C#에서 async/await 메서드는 "비동기 함수"로 불립니다. 기존에 존재하던 Task의 장점을 살려 개발자가 좀 더 쉽게 비동기 작업을 수행할 수 있도록 만든 것이 async/await입니다.
await 연산자는 피연산자가 나타내는 비동기 작업이 완료될 때 까지 수행을 중지합니다. 그리고 await 연산자를 포함한 메서드에 async를 붙여 컴파일러가 해당 함수가 비동기 함수임을 알 수 있게 해줍니다.
async/await 키워드는 "Task"를 사용하므로 여러 스레드를 활용하는게 특징입니다.
중요한 특징은 async/await은 syntax sugar라는 점입니다. 즉, async/await 키워드 없이도 이를 구현할 수 있습니다!
다음 코드가 있다고 가정합니다.
이를 reverse enginerring을 통해 decompile 시키면,
와 같이 변환됩니다. 즉, 컴파일러가 async가 붙은 함수를 class로 구현된 finite state machine으로 변환합니다.
재밌는 건, 구현된 클래스의 메서드를 실행하는 주체가 컴파일러라는 점입니다. 작성한 ExecuteDB async함수의 실행부가 완전히 달라진 것을 확인할 수 있습니다.
Unitask
Task는 C#의 "Awaitable pattern"에 대한 구현체이고, Unitask 또한 C#의 "Awaitable pattern"의 구현체입니다. 따라서, Task와 unitask는 async/await 용법이 같을 뿐 전혀 다르게 동작합니다. 즉, C#에서는 비동기 메커니즘이 어떻게 동작할지에 대한 구현체를 직접 구현할 수 있습니다. async/await은 단순히 async 함수를 await을 분기로하는 state machine으로 변경해줍니다.
다음 시간에는 직접 custom awaitable pattern을 구현하는 LEapsTask를 만들어 더 깊게 이해하는시간을 가져봅니다.