공부 정리/Unity

Synchronous(동기), Asynchronous(비동기), Parallel(병렬) 비교

임녕 2025. 3. 20. 21:20

1. Synchronous(동기)

동기 방식은 작업들이 순차적으로 실행되기 때문에 첫 번째 작업이 완료될 때까지 두 번째 작업이 실행되지 않습니다.

 

* Unity는 기본적으로 Synchronous(동기) 방식으로 동작합니다.

 

예제 스크립트

using UnityEngine;

public class Synchronous : MonoBehaviour
{
    void Start()
    {
        Task1();
        Task2();
    }

    void Task1()
    {
        Debug.Log("Synchronous Task 1 시작");
        System.Threading.Thread.Sleep(2000);
        Debug.Log("Synchronous Task 1 완료");
    }

    void Task2()
    {
        Debug.Log("Synchronous Task 2 시작");
        System.Threading.Thread.Sleep(2000);
        Debug.Log("Synchronous Task 2 완료");
    }
}

 

따로 비동기나 병렬 처리를 하지않은 일반적인 코드 입니다. 위에서부터 Task가 하나하나 실행되고 종료될 것이기 때문에 Task1이 처음부터 끝까지 실행된 후 Task2가 실행됩니다.

 

출력 결과

 


2. Asynchronous(비동기)

비동기 방식은 먼저 진행 중인 작업이 대기 상태에 들어갔을 때 다른 작업도 같이 진행할 수 있습니다.

async / await 또는 Coroutine 사용하여 구현할 수 있습니다. 이번 글에서는 async / await로 작성하고 추후 Coroutine에 대해 알아보는 글에서 두 방식의 차이를 비교해서 다룰 예정입니다.

 

예제 스크립트

using UnityEngine;
using System.Threading.Tasks;

public class Asynchronous : MonoBehaviour
{
    async void Start()
    {
        Task task1 = Task1();
        Task task2 = Task2();

        await task1;
        await task2;
    }

    async Task Task1()
    {
        Debug.Log("Asynchronous Task 1 시작");
        await Task.Delay(2000);
        Debug.Log("Asynchronous Task 1 완료");
    }

    async Task Task2()
    {
        Debug.Log("Asynchronous Task 2 시작");
        await Task.Delay(2000);
        Debug.Log("Asynchronous Task 2 완료");
    }
}

 

비동기로 동작할 함수는 앞에 async를 붙여야하고 비동기 함수를 호출할 때에는 'await 함수명;' 으로 호출해야합니다. await 는 그 함수가 끝나길 기다린다는 키워드 입니다.

 

Task1을 먼저 호출하는 것은 동기 방식과 동일하지만 Task1, Task2를 비동기 방식으로 구현, 호출했기 때문에 

 

* Task1이 대기 상태가 되면 Task1이 대기 상태인 동안 이후 작업인 Task2의 작업을 진행하게 됩니다.

 

위 스크립트 상으로는 Task1이 Log를 출력한 이후 대기상태에 빠지자마자 Task2의 Log가 출력되고 Task2도 거의 동시에 대기 상태에 들어가게 됩니다.

 

거의 동시에 실행, 대기상태가 되기 때문에 2초 정도가 흐르면 Task1과 Task2의 대기 상태가 같이 끝나게되며 이후의 Log가 호출됩니다.

 

출력 결과

 


3. Parallel(병렬)

병렬로 프로그래밍 한다는 것은 간단하게 얘기하면 포함된 모든 작업이 서로에게 간섭하지 않으며 다른 작업의 상태에 상관없이 동시에 진행되는 것입니다.

 

예제 스크립트

using UnityEngine;
using System.Threading.Tasks;

public class Parallel : MonoBehaviour
{
    void Start()
    {
        ParallelTask();
    }

    async void ParallelTask()
    {
        Task task1 = Task.Run(() => Task1());
        Task task2 = Task.Run(() => Task2());

        await Task.WhenAll(task1, task2);
    }

    void Task1()
    {
        Debug.Log("Parallel Task 1 시작");
        System.Threading.Thread.Sleep(2000);
        Debug.Log("Parallel Task 1 완료");
    }

    void Task2()
    {
        Debug.Log("Parallel Task 2 시작");
        System.Threading.Thread.Sleep(2000);
        Debug.Log("Parallel Task 2 완료");
    }
}

 

작업을 실행한다는 의미를 가진 Task.Run() 을 하나의 Task로 만들고 Task.WhenAll() 을 await로 호출하여 모든 Task의 작업을 동시에 실행시키고 모든 Task의 작업이 끝나기를 기다립니다.

 

* 비동기와 비슷한 동작처럼 보일 수 있으나 비동기 프로그래밍은 메인 Thread 하나만 사용하는 반면에 병렬 프로그래밍의 경우 Thread를 여러개 만들어서 각 Thread 들이 작업을 가지고 처음부터 모든 작업이 동시에 실행됩니다.

 

출력 결과

 

* 병렬 프로그래밍의 경우 Task 코드나 상황, 환경에 따라 출력 순서가 바뀔 수 있습니다.

 


4. 중요한 점 정리

1) 기본 개념

- Synchronous(동기): Unity의 코드가 기본적으로 동작하는 방식, 위에서부터 한 작업 씩 끝내며 진행된다.

- Asynchronous(비동기): 먼저 실행 중인 작업이 대기 상태가 된다면 대기하는 동안 다음 작업을 진행한다.

- Parallel(병렬): 모든 Task가 동시다발적으로 실행된다.

 

* Unity는 기본적으로 메인 Thread 하나만으로 동작하고 비동기, 병렬 프로그래밍 코드를 작성하려면 개발자가 직접 구현해줘야 합니다.

 

2) Asynchronous(비동기) 와 Parallel(병렬)의 공통점, 차이점

공통점: 작업을 동시에 진행, 처리할 수 있으므로 전체적인 작업 시간이 줄어듭니다.

차이점: 비동기 방식은 메인 Thread 하나만 가지고 왔다갔다 하는 반면에 병렬 방식은 여러 Thread가 각자 작업을 수행합니다. (여러 Thread로 작업하는 편이 CPU 리소스를 효율적으로 사용할 수 있다고 합니다.)

 

* 비동기의 경우 await로 대기를 처리하고 병렬 프로그래밍의 경우 Task.WhenAll()로 대기를 처리합니다.

 

3) 상황 별 사용해야할 프로그래밍 방식

Asynchronous(비동기): 웹 API요청, 파일 다운로드, 네트워크 통신 처럼 대기 시간이 있는 작업에 사용

Parallel(병렬): 복잡한 계산, 이미지 처리, 대량의 데이터 처리에 사용

 

+ 가벼운 예시로 비교하기

Synchronous(동기)
'한 명의 요리사가 요리 하나를 마칠 때 까지 다른 요리는 시작하지 않는다.'
요리사1: 라면 물 올리기 → 물이 끓을 때 까지 라면 앞에서 대기 → 라면 완성 → 야채 손질 시작 → 야채 손질 끝

Asynchronous(비동기)
'한 명의 요리사가 여러 요리를 왔다갔다 한다'
요리사1:  라면 물 올리기 → 끓기를 기다리는 동안 야채 손질 시작  → 물이 끓으면 다시 라면으로 복귀 → 라면 완성 → 야채 손질 끝

Parallel(병렬)
'여러명의 요리사가 동시에 한 요리 씩 담당한다.'

요리사1:  라면 물 올리기 → 물이 끓을 때 까지 라면 앞에서 대기 → 라면 완성

요리사2:  야채 손질 시작 → 야채 손질 끝

요리사 == Thread, 요리 == Task 입니다.