[C# 기초] 13. 테스크(Task)


쓰레드의 일종으로 비동기 코드를 실행할 수 있도록 고안된 것이 테스크(Task)입니다.

Task


CPU가 발전하면서 클럭을 높이는 방향에는 한계에 다다르자, 하나의 CPU안에 여러개의 코어를 집적하는 방향으로 제품을 향상시키기 시작했습니다. 이러한 하드웨어의 변화에 맞춰 소프트웨어도 변화를 최대로 활용할 수 있는 방법이 등장하고 있습니다.

.NET Framework 에는 System.Threading.Tasks 에는 병행성 코드나 비동기 코드의 실행을 돕는 클래스들이 들어 있습니다. (Task 또한 내부적으로 Thread로 구현됩니다)

Task 클래스를 이용하여 비동기(Asynchronous) 코드를 작성할 수 있습니다. Task 클래스는 코드의 비동기 실행 결과를 얻을 수 있습니다. Task 클래스는 비동기로 수행할 코드를 Action 델리게이트로 주는 반면 Task 는 Func 델리게이트로 줍니다. 즉 Task 비동기 작업이 끝나면 Task<>.Result 프로퍼티에 값을 반환하게 됩니다.

예제 코드:

using System.Threading.Tasks;
using System.Threading;
 
class Program
{
    static void ActionMethod()
    {
        Thread.Sleep(1000);
        Console.WriteLine("ActionMethod Call");
    }

    static int FuncMethod(object a)
    {
        Thread.Sleep(500);
        Console.WriteLine("FuncMethod Call");
        return (int)a+5;
    }

    static void Main(string[] args)
    {
        Task task = new Task(ActionMethod);
        task.Start();                     // task는 비동기 호출로 1초후 완료된다.
        Console.WriteLine("Main Logic");  //Main Logic 문구가 바로 출력된다.

        Task int task2 = new Task int (FuncMethod, (object)10);
        // 매개변수와 반환값을 가진 메소드 사용방법
        task2.Start();
        task2.Wait(); // task2가 메소드가 완료될때 까지 대기
        Console.WriteLine("{0}", task2.Result); // 반환값 출력

        Console.WriteLine("Main Logic2");

        task.Wait(); //task의 메소드가 완료될때 까지 대기

        // Main Logic
        // FuncMethod Call
        // 15
        // Main Logic2
        // ActionMethod Call
    }
}

결과를 보면 아시겠지만 실행해보면 Task 클래스에 의해 비동기 호출이 이루어짐을 알 수 있습니다.

Parallel


Parallel 클래스는 좀더 쉽게 병렬처리를 하고 싶은 메소드를 처리할 수 있게 도와줍니다. Parallel.For() 메소드는 주어진 델리게이트에 대하여 병렬로 호출합니다. 몇개의 쓰레드를 사용할 지는 내부적으로 판단하여 알아서 최적화하여 결정합니다.

예제 코드:

using System.Threading.Tasks;
using System.Threading;
 
class Program
{
    static void ActionMethod(int num)
    {
        Thread.Sleep(1000);
        Console.WriteLine("ActionMethod Call {0}", num);
    }

    static void Main(string[] args)
    {
        Parallel.For(0, 100, ActionMethod); // 50 25 1 75 0 26 ....
    }
}

직접 실행해보면 아시겠지만, 함수를 100번 호출하면서 병렬처리를 하기 때문에 순서가 뒤죽박죽이 되었고 10번 호출할때, 100번호출할때 매개변수값을 바꿔보면 처리방식이 조금씩 달라짐이 보입니다. 그리고 함수호출 한번당 1초정도 소요되게 코드를 짰지만, 병렬처리를 하면서 훨씬 빠르게 해당 프로그램이 종료됨을 보실 수 있습니다.


비동기 코드를 작성할 때 등장하는 await/async 키워드를 활용하면서 사용해야 하는데 기본 문법에서는 다루지 않았습니다. 이런 개념도 있구나 정도로 넘어가주심이 좋을 것 같습니다.