[C# 강좌 - 55] 키워드 async, await


Study/C#  2019. 5. 20. 22:34

안녕하세요. 명월입니다.

 

이 글에서는 async와 await를 소개하겠습니다.

async와 await은 Task 클래스와 매우 밀접한 관계가 있으므로 먼저 Task 클래스의 글을 함께 보면 이해하기 쉽습니다.

링크 - [C# 강좌 - 54]Task 클래스

 

먼저 async 키워드는 void로 사용하는 방법과 Task클래스와 사용하는 방법이 두가지로 나누어져 있습니다.

먼저 void로 사용하는 방법입니다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace Example
{
  class Program
  {
    // 단독으로 사용할 때는 void를 사용한다.
    static async void AsyncTest()
    {
      var task = new Task<int>(() =>
      {
        int sum = 0;
        for (int i = 0; i < 10; i++)
        {
          sum += i;
          // 0.1초 단위로 1씩 증감
          Console.WriteLine(i);
          Thread.Sleep(100);
        }
        return sum;
      });
      task.Start();
    }
    static void Main(string[] args)
    {
      // Task 리턴식이 없기 때문에 제어할 수가 없다.
      AsyncTest();
      Console.WriteLine("Press Any Key...");
      Console.ReadKey();
    }
  }
}

위 소스를 보면 async 메서드에 task를 넣고 실행했습니다. 사실 async - void는 그냥 비동기입니다. ThreadPool로 만드는 것과 차이가 없습니다.

별 의미도 없고 그냥 위처럼 만든다면 그냥 async없이 Task 클래스만 사용하는 게 맞습니다. 실제 저렇게 코딩하면 Visual studio에서도 warning 메시지 나옵니다.

warning 메시지 뜻은 조금 다른 내용인데 비슷한 말입니다.

 

그러므로 async 키워드는 리턴 값이 Task으로 사용하는게 맞는 것 같습니다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace Example
{
  class Program
  {
    // 단독으로 사용할 때는 void를 사용한다.
    static async Task<int> AsyncTest()
    {
      var task = new Task<int>(() =>
      {
        int sum = 0;
        for (int i = 0; i < 10; i++)
        {
          sum += i;
          // 0.1초 단위로 1씩 증감
          //Console.WriteLine(i);
          Thread.Sleep(100);
        }
        return sum;
      });
      task.Start();
      //외부에서 await에서 기다린다.
      await task;
      // Wait가 호출되면 통과된다.
      Console.WriteLine(task.Result);
      return 10;
    }

    static void Main(string[] args)
    {
      var task = AsyncTest();
      Console.WriteLine("pass await 1");
      // Wait
      task.Wait();
      Console.WriteLine("pass await 2");
      //결국 Return까지 기다린다.
      int result = task.Result;
      //결과 같은 10이다.
      Console.WriteLine(result);
      Console.WriteLine("Press Any Key...");
      Console.ReadKey();
    }
  }
}

우리가 쓰레드를 만들 때 실제 호출하는 쪽에서 쓰레드 안의 제어를 하기 쉽지 않습니다. 문제는 동기화 때문입니다.

그러나 async - await을 쓰면 제어가 가능해 집니다.

 

위 예제는 Main에서 AsyncTest를 호출했습니다. AsyncTest 메서드 안에서는 task가 실행됩니다. 물론 비동기로 실행됩니다. 여기서 이 쓰레드를 잠깐 멈추게 하고 싶습니다. 그럼 외부에서 Wait함수를 부르면 되는데 이 때 await 키워드로 멈추는 포인트를 지정할 수 있는 것입니다.

이렇게 되면 순서는 결과의 순서는 「pass await 1」 => 「45」 => 「pass await 2」 => 「10」 으로 출력이 됩니다.

다음은 async와 더불어 많이 사용하는 ContinueWith 함수를 소개하겠습니다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace Example
{
  class Program
  {
    static async Task<int> AsyncTest()
    {
      var task = new Task<int>(() =>
      {
        int sum = 0;
        for (int i = 0; i < 10; i++)
        {
          sum += i;
          // 0.1초 단위로 1씩 증감
          Console.WriteLine(i);
          Thread.Sleep(100);
        }
        return sum;
      });

      task.Start();
      await task;
      task = new Task<int>(() =>
      {
        int sum = 0;
        for (int i = 10; i < 20; i++)
        {
          sum += i;
          Thread.Sleep(100);
        }
        return sum;
      });
      task.Start();
      // await이 위에 있지만 결국에는 145가 리턴된다.
      return task.Result;
    }

    static void Main(string[] args)
    {
      //AsyncTest 끝나면 이어서 실행된다. 즉, AsyncTest가 종료되면 실행되는 Thread
      var continueTask = AsyncTest().ContinueWith(task =>
      {
        return task.Result;
      });
      //continueTask 끝날때까지 기다린다.
      Console.WriteLine(continueTask.Result);

      Console.WriteLine("Press Any Key...");
      Console.ReadKey();
    }
  }
}

ContinueWith 함수는 Task를 연달아 붙여서 사용할 때 사용합니다. 이 뜻은 Task => Wait => Result => Task => Wait => Result 이지만 위처럼 ContinueWith함수를 써서 간단하게 처리를 했습니다.

ContinueWith는 C#의 콜백 함수라고도 부르는 사람도 있습니다.

 

여기까지 async와 await 설명이었습니다.

 

궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.