async 및 await 키워드

C# 5.0에는 async 및 await가 포함되어 있습니다.이 두 키워드는 동기화 방식에 따라 비동기 코드를 쓰기에 더욱 편리하게 할 수 있다.즉, 당신을 더욱 편리하게 하는 비동기 프로그래밍이다.일반적인 쓰기 형식은 다음과 같습니다.
   
   
   
   
  1. var result = await expression
  2. statement(s); 

이런 작법은 다음과 같다.
   
   
   
   
  1. var awaiter = expression.GetAwaiter(); 
  2. awaiter.OnCompleted (() => 
  3. var result = awaiter.GetResult(); 
  4. statement(s); 
  5. ); 

여기의 expression은 보통Task나Task이지만, 사실상 await를 사용할 수 있는 대상을 스스로 정의할 수 있다.하지만 일정한 조건을 충족시켜야 한다.먼저 하나의 예를 보아라. 
   
   
   
   
  1. static void Main(string[] args) 
  2.        { 
  3.            DisplayPrimesCount(); 
  4.            Thread.Sleep(5000);//  
  5.        } 
  6.        static Task<int> GetPrimesCountAsync(int start, int count) 
  7.        { 
  8.            return Task.Run(() => 
  9.            ParallelEnumerable.Range(start, count).Count(n => 
  10.            Enumerable.Range(2, (int)Math.Sqrt(n) - 1).All(i => n % i > 0))); 
  11.        } 
  12.        static async void DisplayPrimesCount() 
  13.        { 
  14.            int result = await GetPrimesCountAsync(2, 1000000);//  
  15.            Console.WriteLine(result); 
  16.        } 

이것은 비교적 흔히 볼 수 있는 서법이다.
GetPrimescountAsync 메서드를 비동기적으로 실행하려면 먼저 GetPrimescountAsync에서 대기 가능 객체를 반환해야 합니다.위 코드에서 GetPrimesCountasync는 Task 유형을 반환합니다.그리고 await 방법을 사용하려면 async 키워드를 표시해야 합니다.또한 await Get Primes Count Async(2,1000000)를 볼 수 있다.이 문장은 Task가 아닌 int로 되돌아옵니다.
이러한 코드는 다음과 같은 자연 언어로 설명되어 있습니다.
DisplayPrimesCount를 호출하면 DisplayPrimesCount에서 새 Task를 실행합니다. 이task는 소수의 개수를 계산합니다.작업이 완료되면 계산된 값이 반환되고 Task 객체에 반환된 값이 호출자에게 반환됩니다.호출자가 이 Task 값을 획득한 후 Task 의 Result 값을 꺼냅니다.
프로그램 논리가 await Get Primes Count Async 방법을 만났을 때, 스레드는 비동기적인 실행이 끝날 때까지result 값을 받은 후에 계속 실행될 것입니다.
본질적으로 await와 async의 출현도 하나의 문법사탕일 뿐이다. 그러나 이 문법사탕은 비동기 프로그래밍을 더욱 우아하게 할 수 있다. 원래의 EAP와 APM 같은 곳곳의 BeginXX, EndXX의 추악한 모델을 직접 버리고 생산력을 향상시켰다.
await 방법을 사용할 수 있습니다. 되돌아오는 값은 awaitable 대상이어야 합니다. 사용자 정의 awaitable 대상은 비교적 번거롭습니다. 한 대상은 다음과 같은 조건을 충족시켜야 합니다.
  • GetAwaiter () 방법이 있어야 하며 확장 방법이나 실례 방법이 모두 가능해야 한다
  • GetAwaiter () 방법의 반환 값은 awaiter 대상이어야 합니다.하나의 대상이 awaiter 대상이 되려면 다음과 같은 조건을 만족해야 한다.
  • 이 대상은 인터페이스INotifyCompletion 또는 ICriticalNotifyCompletion
  • 을 실현한다.
  • IsCompleted 속성
  • 이 있어야 합니다.
  • void나 다른 반환 값을 되돌릴 수 있는GetResult() 방법이 있어야 합니다.


  • 마이크로소프트는 상술한 조건을 충족시키는 인터페이스를 제시하지 않았기 때문에 스스로 이런 인터페이스를 실현할 수 있다. 
       
       
       
       
    1. public interface IAwaitable<out TResult> 
    2.     { 
    3.         IAwaiter GetAwaiter(); 
    4.     } 
    5.     public interface IAwaiter<out TResult> : INotifyCompletion // or ICriticalNotifyCompletion 
    6.     { 
    7.         bool IsCompleted { get; } 
    8.         TResult GetResult(); 
    9.     } 

    람다 표현식에 대해 await를 직접 사용할 수 없기 때문에 프로그래밍을 통해 기교적으로 이 기능을 실현할 수 있다.예를 들어 어떤 Func 의뢰에 대한 확장 방법은 다음과 같습니다. 확장 방법은 최고급 정적 클래스에서 정의해야 합니다. 
       
       
       
       
    1. public static class FuncExtensions 
    2.     { 
    3.         public static IAwaiter GetAwaiter(this Func function) 
    4.         { 
    5.             return new FuncAwaiter(function); 
    6.         } 
    7.     } 
    8.     public interface IAwaitable<out TResult> 
    9.     { 
    10.         IAwaiter GetAwaiter(); 
    11.     } 
    12.     public interface IAwaiter<out TResult> : INotifyCompletion // or ICriticalNotifyCompletion 
    13.     { 
    14.         bool IsCompleted { get; } 
    15.         TResult GetResult(); 
    16.     } 
    17.     internal struct FuncAwaitable : IAwaitable 
    18.     { 
    19.         private readonly Func function; 
    20.         public FuncAwaitable(Func function) 
    21.         { 
    22.             this.function = function; 
    23.         } 
    24.         public IAwaiter GetAwaiter() 
    25.         { 
    26.             return new FuncAwaiter(this.function); 
    27.         } 
    28.     } 
    29.     public struct FuncAwaiter : IAwaiter 
    30.     { 
    31.         private readonly Task task; 
    32.         public FuncAwaiter(Func function) 
    33.         { 
    34.             this.task = new Task(function); 
    35.             this.task.Start(); 
    36.         } 
    37.         bool IAwaiter.IsCompleted 
    38.         { 
    39.             get 
    40.             { 
    41.                 return this.task.IsCompleted; 
    42.             } 
    43.         } 
    44.         TResult IAwaiter.GetResult() 
    45.         { 
    46.             return this.task.Result; 
    47.         } 
    48.         void INotifyCompletion.OnCompleted(Action continuation) 
    49.         { 
    50.             new Task(continuation).Start(); 
    51.         } 
    52.     } 
    53.      
    54. main : 
    55.  
    56.   static void Main(string[] args) 
    57.         { 
    58.             Func(() => { Console.WriteLine("await..");return 0;}); 
    59.             Thread.Sleep(5000);//  
    60.         } 
    61.         static async void Func(Func<int> f) 
    62.         { 
    63.             int result = await new Func<int>(f); 
    64.             Console.WriteLine(result); 
    65.         } 

    여기에서:
    Func 메서드는 확장 메서드GetAwaiter가 실행되고 반환 값 유형이 직접 정의한 IAwaitable 유형이기 때문에 비동기적으로 실행할 수 있습니다.
    물론 더 간단한 방법은 마이크로소프트가 제공하는 Task 대상을 이용하여 람다 표현식을Task 유형으로 되돌려주면 된다는 것이다. 
       
       
       
       
    1. static void Main(string[] args) 
    2.         { 
    3.             Func(() => 
    4.             { 
    5.                 return Task<int>.Run<int>(() => { return Enumerable.Range(1,100).Sum(); }); 
    6.             }); 
    7.             Thread.Sleep(5000);//  
    8.         } 
    9.         static async void Func(Funcint>> f) 
    10.         { 
    11.             int result = await f(); 
    12.             Console.WriteLine(result); 
    13.         } 
    ---------------------------------

    참조 자료: C# 5.0 IN A NUTSHELL
    http://weblogs.asp.net/dixin/archive/2012/11/08/understanding-c-async-await-2-awaitable-awaiter-pattern.aspx

    좋은 웹페이지 즐겨찾기