본문 바로가기

프로그램/유니티 C# 강좌

[유니티 C# 강좌] 19. 비동기 프로그래밍(Asynchronous Programming)

728x90
반응형

1. 동기 프로그래밍, 비동기 프로그래밍

 

동기 프로그래밍(Synchronous Programming)이란, 프로그램이 실행되는 도중 어떤 작업을 요청하면, 그 작업이 종료될 때까지 기다렸다가 다음 작업을 하는 것을 의미합니다.

동기 방식은 요청과 결과가 동시에 일어나기 때문에, 설계가 매우 간단하고 직관적이지만, 작업이 완료될 때까지 프로그램이 대기해야 합니다.

 

비동기 프로그래밍(Asynchronous Programming)란, 프로그램이 실행 되는 도중 어떤 행위를 요청한 뒤 그 결과는 기다리지 않고, 다음 동작을 하는 것을 의미합니다.

비동기 프로그래밍은 설계 단계에서 동기보다 복잡하고, 요청한 작업이 완료될 때는 별도로 확인해야 하지만, 요청한 자료가 오래 걸린다고 하더라도 다른 작업을 계속할 수 있기 때문에 자원을 효율적으로 사용할 수 있습니다.

 

코더제로 유니티 C# 강좌 비동기 프로그래밍(Asynchronous Programming)  동기 프로그래밍, 비동기 프로그래밍
그림. 동기 프로그래밍, 비동기 프로그래밍

2. Task 클래스

2.1 Task 클래스

 

일반적으로 하나의 프로세스는 하나의 스레드를 가지고 작업을 수행합니다.

하지만 멀티 스레드(Multi Thread)란 하나의 프로세스에서 둘 이상의 스레드가 동시에 작업을 수행하느것을 의미합니다.

Task 클래스는 .Net 4.0부터 도입된 클래스로 멀티 스레드에서 비동기 작업을 실행합니다.

 

Task 클래스는 using 지시문을 사용하여 System.Threading.Tasks를 지시해 주어야 합니다.

 

네임스페이스

using System.Threading.Tasks;

 

Task 사용하는 방법은 직접 호출, Action 대리자 사용, 대리자, 람다식, 람다와 익명 메서드 등이 있습니다.

 

using UnityEngine; 
using System.Threading.Tasks; 
using System; 

public class TaskExample : MonoBehaviour 
{ 
    void Start() 
    { 
        Task.Factory.StartNew(() => { Debug.Log("Task"); }); // 직접 호출, 스레드 생성과 시작 
        Task task2 = new Task(new Action(DebugLog)); // Action 대리자 
        task2.Start(); // Task 스레드 시작 

        Task task3 = new Task(delegate { DebugLog(); });  // 대리자 
        task3.Start(); // Task 스레드 시작  

        Task task4 = new Task(() => DebugLog()); // 람다식 
        task4.Start(); // Task 스레드 시작  

        Task task5 = new Task(() => { DebugLog(); }); // 람다와 익명 메서드 
        task5.Start(); // Task 스레드 시작 


        task2.Wait(); // Task가 끝날 때까지 대기  
        task3.Wait(); // Task가 끝날 때까지 대기 
        task4.Wait(); // Task가 끝날 때까지 대기 
        task4.Wait(); // Task가 끝날 때까지 대기 
    } 

    void DebugLog() 
    { 
        Debug.Log("Task"); 
    } 
}

 

Task 메서드

설명

Factory.StartNew

스레드 생성과 시작

Start

Task 스레드 시작

Wait

Task 끝날 때까지 대기

 

2.2 Task<T> 클래스

 

일반 Task는 변환값을 받지 못하지만, 제네릭 Task를 사용하면 변환값을 얻을 수 있습니다.

Result라는 속성으로 변환값을 얻을 수 있습니다.

 

using UnityEngine; 
using System.Threading.Tasks; 

public class GenericTaskExample : MonoBehaviour 
{ 
    void Start() 
    { 
        Task<int> task = Task.Factory.StartNew<int>(() => GetSize("GenericTask")); 
        int result = task.Result; 
        Debug.Log(result); // 출력 : 11  
    } 

    int GetSize(string data) 
    { 
        return data.Length; 
    } 
}

 

2.3 Task 작업 취소

 

비동기 작업 취소하기 위해서는 Cancellation Token을 사용하는데,  작업 취소와 관련된 타입은 CancellationTokenSource 클래스와 CancellationToken 구조체입니다. 

 

네임스페이스

using System.Threading;
using System.Threading.Tasks;

 

작업을 취소하는 일반적인 절차

① CancellationTokenSource 클래스 필드에서 선언

② CancellationTokenSource 객체를 생성

③ 비동기 작업 메서드 안에서 작업이 취소되었는지를 체크하는 코드(.Token.IsCancellationRequested)를 넣으며,

④ 취소 명령이 들어오면 CancellationTokenSource의 Cancel() 메서드를 호출해 작업 취소를 요청

 

using UnityEngine; 
using System.Threading; 
using System.Threading.Tasks; 

public class CancellationTokenExample : MonoBehaviour 
{ 

    CancellationTokenSource m_CancelTokenSource; // ① CancellationTokenSource 클래스 필드에서 선언  
    Task<int> m_Task; 

    void Start() 
    { 
        m_CancelTokenSource = new CancellationTokenSource();  // ② CancellationTokenSource 객체를 생성 
        CancellationToken cancellationToken = m_CancelTokenSource.Token; 
        m_Task = Task.Factory.StartNew(TaskMethod, cancellationToken); 
    } 

    void Update() 
    { 
        if (Input.GetKeyDown(KeyCode.C)) 
        { 
            m_CancelTokenSource.Cancel(); // ④ CancellationTokenSource의 Cancel() 메서드를 호출해 작업 취소 
            if (m_Task != null) 
            { 
                Debug.Log("Count : " + m_Task.Result); 
            } 
        } 
    } 

    private int TaskMethod() 
    { 
        int count = 0; 
        for (int i = 0; i < 10; i++) 
        { 
            if (m_CancelTokenSource.Token.IsCancellationRequested) // ③ 비동기 작업 메서드 안에서 작업이 취소되었는지를 체크 
            { 
                break; 
            } 

            ++count; 
            Thread.Sleep(1000); 
        } 
        return count; 
    } 
}

 

3. async, await

 

C# 5.0부터 추가된 키워드 async와 await는 비동기 프로그래밍을 지원하는 키워드입니다.

async는 해당 메서드가 awiat를 가지고 있음을 알려 줍니다.

 

 

Task 메서드

설명

Run

비동기 시작

FromResult

비동기 시작 후 결과값을 얻음.

 

using UnityEngine; 
using System.Threading; 
using System.Threading.Tasks; 

public class AsyncAwaitExample : MonoBehaviour 
{ 
    void Start() 
    { 
        TaskRun(); 
        TaskFromResult(); 
    } 

    async void TaskRun() 
    { 
        var task = Task.Run(() => TaskRunMethod(3)); 
        int count = await task; 
        Debug.Log("Count : " + task.Result); // 출력 : Count : 3  
    } 

    private int TaskRunMethod(int limit) 
    { 
        int count = 0; 
        for (int i = 0; i < limit; i++) 
        { 
            ++count; 
            Thread.Sleep(1000); 
        } 

        return count; 
    } 

    async void TaskFromResult() 
    { 
        int sum = await Task.FromResult(Add(1, 2)); 
        Debug.Log(sum); // 출력 : 3  
    } 

    private int Add(int a, int b) 
    { 
        return a + b; 
    } 
}

 

728x90
반응형