Post List

2015년 9월 15일 화요일

C# async / await 를 이용한 비동기 프로그래밍

* 비동기 프로그램의 필요성

  - 예전에는 PC에 CPU 및 내부 core가 1개라서 동기식으로 프로그램을 작성하여도 아무런 문제가 없었습니다.
  - Single core에 multi-thread 프로그램을 작성한들 최종 결과가 나오는 시간은 더 빨라지지 않습니다.

  - 하지만 CPU도 이제는 multi-core 가 되었기 때문에 계속해서 동기식 프로그램으로 작성을 한다면,
  - 해당 CPU에서 하나의 core만을 사용해서 application이 동작하게 되므로 비동기식 프로그램으로 작성을 하면 성능을 향상 시킬 수 있습니다.

* 기존의 비동기 프로그램 방법

  - 비동기 프로그램 ? 그거 그까이꺼 대충 그냥 이렇게 하면 되자나요.

  1. 함수 호출시 별도의 thread를 만들어서 callback 함수를 전달해야 합니다.
  2. 해당 함수는 작업이 끝난 후 callback 함수를 호출합니다.
  3. 원래 thread 에서는 비동기 작업과는 별개인 작업들을 진행하고 있습니다.
  4. 비동기 작업의 결과가 필요한 시점에서는 해당 작업이 끝났는지 기다려야 합니다.
  5. 보통 기다리기 위해서는 while 문등을 이용하여 무한-loop를 돌고 있으면서 기다립니다.
  6. 기다리는 동안의 CPU도 아깝자나요. 그러니깐 기다리는 것도 특정 간격으로 기다리면서 해당 threadsleep 시키면서 다른 thread 에게 작업을 양보해주는 센스 정도야 발휘해 줘야 겠죠 ?

  - 어때요 ? 참 쉽죠 ? ㅎ
  - 위 방법 말고도 다른 방법들이 많습니다.
  - callbackcallback 을 넣고 하는 방법도 있구요.
  - 결과를 특정 위치에 기록하고, 그 결과를 pulling 하는 곳에서는 해당 값을 보고 진행을 하는 방법도 있구요.
  - pulling 조차 하지않고 저기서도 callback을 이용해서 다음 진행을 할 수도 있습니다.

  - 어쨌거나 위 사항들을 여러 개의 메서드 들로 쪼개서 직접 구현해야 합니다.

  - 구현이야 개발자는 그게 일이니깐요. 그냥 하면 될껀데,
  - 개발자들의 업무 특성상 코딩을 하는 시간 보다는 코드를 읽는 시간이 훨씬 더 많습니다.
  - 위 방법으로 구현된 코드를 따라 가면서 읽는건 결코 쉬운 일이 아닙니다.

* async / await 의 등장배경

  - 동기식 프로그램 같은 코드의 흐름으로 작성이 가능하면서도,
  - 동작은 비동기식으로 하는 기능을 제공해 줍니다.
  - 작성이 간편해 진것은 물론이고,
  - 해당 함수의 사용 및 코드를 읽기도 훨씬 편해졌습니다.
  - .NET Framework 4.5부터 지원합니다.
  - 기존 class library 들 대부분의 메서드에서 ~Async를 접미사로 붙인 메서드들이 제공됩니다.
    해당 메서드들은 Task, Task<TResult>return 하는 async 메서드들 입니다.

* async

  - 메서드 선언시 붙여 줄 수 있는 키워드 입니다.
  - 비동기로 해당 메서드를 실행하라는 말입니다.
  - 비동기 라는 말이 어려우면 별도 thread로 해당 함수를 실행하라는 것으로 생각하셔도 됩니다.
  - 하지만 실제로 추가로 thread를 생성해서 실행시키지는 않습니다.
  - 내부적으로 windows 가 해당 code를 어떻게 실행하느냐 까지 설명하려면, 상당히 복잡해 집니다.
  - 한마디로 만지면 커집니다. ;;;
  - 내부에 await 가 사용되고 있다는 뜻을 내포합니다.
    (내부에 await가 없는데 async로 선언을 한 경우에는 async가 없는거랑 똑같이 취급됩니다.)
  - async 가 선언된 메서드의 return 타입으로는 Task, Task<TResult> 만을 허용합니다.
  - 매개변수로 ref, out 의 사용이 불가능 합니다.

* await

  - async 로 선언됨 함수의 결과를 기다리라는 뜻입니다.
    (return 타입이 Task, Task<TResult> 인 함수들만 됩니다.)
  - 결과를 받아 올 때까지는 해당 메서드의 나머지 작업들은 더 이상 실행되지 않습니다.
  - try 절의 catch, finally 내부에서는 사용이 불가능 합니다. try 내부에서는 가능합니다.
  - lock, unsafe 내부에서도 사용이 불가능 합니다.

* 예제

  - 예제를 console 로 작성해도 똑같겠지만, console 로 하려고 C#을 하진 않자나요. 간단한 Form 을 하나 작성해보겠습니다.

  - 먼저 그림과 같이 버튼 하나와 textbox 만을 가진 form을 생성합니다.
    (필자는 RichTextBox로 했습니다.)

 

  - 버튼 click event 를 추가하여 아래 code와 같이 비동기 메서드를 구현합니다.



  - 결과는 다음과 같습니다. (첫 두줄의 순서는 CPU scheduling에 따라 달라 질 수 있습니다.)




* 결과 분석

  -  btn_start_Click() 에서 GetWebSync() 메서드를 호출했습니다.
  - 해당 메서드는 async로 선언되었으므로 비동기로 수행을 하며 btn_start_Click() 는 아래에 작업들을 실행시킵니다.
  - GetWebAsync() 내부에서 client.GetStringAsync() 라는 비동기 메서드를 호출합니다.
  - 해당 메서드는 string 이라는 결과 값이 있는 메서드 이므로 별도의 Task에 할당합니다.
  - 그 결과값이 필요한 시점 이전까지의 작업을 수행합니다.
  - 결과값이 필요한 시점에서는 await를 이용하여 Task의 값을 기다립니다.
  - 기다리는 시점 이후의 코드들은 결과가 올 때까지 실행되지 않습니다.



댓글 없음:

댓글 쓰기