본문 바로가기

C#

[C#] Async,Await의 내부

원본 https://github.com/pjc0247/behind_async_await


behind_async_await

C#은 stackless한 코루틴을 지원합니다. 어케 가능한지 아라보자

static async void Foo()
{
    Console.WriteLine("A");

    await Task.Delay(1000);

    Console.WriteLine("B");

    await Task.Delay(1000);

    Console.WriteLine("C");
}

아래의 코드는 Foo 함수를 디컴파일 한 결과물을 기반으로 다시 작성되었습니다. 대충 필요한것만 남기고 생략함

class MyAsync : System.Runtime.CompilerServices.IAsyncStateMachine
{
    internal AsyncVoidMethodBuilder builder { get; set; }
    internal int ptr { get; set; }

    public void MoveNext()
    {
        var _this = this;

        switch(ptr)
        {
            case 0:
                {
                    Console.WriteLine("A : " + ptr.ToString());
                    var task = Task.Delay(1000).GetAwaiter();
                    builder.AwaitUnsafeOnCompleted(ref task, ref _this);

                    ptr++;
                    break;
                }

            case 1:
                {
                    Console.WriteLine("B : " + ptr.ToString());
                    var task = Task.Delay(1000).GetAwaiter();
                    builder.AwaitUnsafeOnCompleted(ref task, ref _this);

                    ptr++;
                    break;
                }

            case 2:
                {
                    Console.WriteLine("C : " + ptr.ToString());
                    break;
                }
        }
    }

    public void SetStateMachine(IAsyncStateMachine stateMachine)
    {
    }
}

var async = new MyAsync();
var builder = AsyncVoidMethodBuilder.Create();
async.ptr = 0;
async.builder = builder;
builder.Start(ref async);
  • async가 붙은 메소드는 IAsyncStateMachine를 구현하는 클래스로 변환됩니다.
  • IAsyncStateMachine는 MoveNext 메소드를 정의합니다.
  • MoveNext는 계속 실행됩니다. MoveNext 내부의 상태(실행 포인터) 관리는 클래스 내부의 구현체에서 직접 해야합니다.
  • AwaitUnsafeOnCompleted는 어떠한 Task가 완료되면 다시 MoveNext를 호출하도록 예약하는 역할을 합니다.
  • 이 작업이 반복되면서 await/async가 동작하게 됩니다.

결론

  • stackless 코루틴은 stackful 코루틴처럼 사기군같은 컨텍스트 스위칭 없이 자체 상태머신을 이용한 멀쩡해보이는 방법으로 구현한다.
  • 컴파일러가 자동으로 코드를 길게 풀어준다.
  • 사기군같은 stackful 코루틴은 https://github.com/pjc0247/jwgtrich


'C#' 카테고리의 다른 글

[C#] Linq.Expression Snippets  (0) 2016.09.22
[C#] async void / Task  (0) 2016.07.18
[HTTP] 웹소켓 핸드쉐이킹  (2) 2016.06.28
[C#] Mono 환경인지 구분하기  (0) 2016.06.21
[NUnit] Callback 방식의 API 테스트하기  (1) 2016.06.16