코루틴과 멀티쓰레드
코루틴을 통하여 마치 멀티 쓰레딩을 할 수 있지만, 사실 이는 멀티 쓰레딩과는 거리가 있습니다.
코루틴(Coroutine)
매 프레임마다 스케쥴링으로 관리가 됩니다. 매 프레임에서 IEnumerator의 반복기 형태로 호출되며 체크합니다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CoroutineTest : MonoBehaviour
{
private void Start()
{
StartCoroutine(DelayedHelloWorld(2f)); // 2초 뒤에 출력
StartCoroutine(DelayedHelloWorld(1f)); // 1초 뒤에 출력
StartCoroutine(DelayedHelloWorld(3f)); // 3초 뒤에 출력
}
private IEnumerator DelayedHelloWorld(float seconds)
{
yield return new WaitForSeconds(seconds);
Debug.Log("Hello World!");
}
}
위와 같이 코루틴을 사용하면 각각 따로 함수가 호출된 것처럼 사용할 수 있습니다. 이는 마치, 쓰레드와 사용했을 때와 같거나 또는 유사한 결과가 나옵니다. 유사한다는 이유는 아래에서 알 수 있습니다.
쓰레드(Thread)
쓰레드는 하나의 프로세스 단위를 말합니다. 하나의 프로그램에서 하나의 프로세스만 쓰는 것이 아닌 여러 개를 사용할 수 있습니다. 반면에 유니티는 단일 쓰레드로만 작동하는 엔진이어서 유니티로만 C#을 공부했다면 모르거나 헷갈려하는 사람들이 꽤있습니다.
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
public class TheadTest : MonoBehaviour
{
private void Start()
{
Thread thread1 = new Thread(() => DelayedHelloWorld(2f));
Thread thread2 = new Thread(() => DelayedHelloWorld(1f));
Thread thread3 = new Thread(() => DelayedHelloWorld(3f));
thread1.Start();
thread2.Start();
thread3.Start();
}
private void DelayedHelloWorld(float seconds)
{
Thread.Sleep((int)(1000 * seconds));
Debug.Log("Hello World!");
}
}
차이점
결과를 두고 봤을 때는 같은 결과를 출력하고 있지만, 코루틴은 하나의 쓰레드에서 스케쥴링으로 관리가 되고 있습니다. 반면 멀티 쓰레드로 구성을 했을 경우, 예외 상황이 많이 생기며 (주로 데드락), 각 프로세스마다 우선권을 주기 위해서 관리되는 기법들도 있습니다. (Join() 호출, 뮤텍스, 셰마포어)
이해
- 코루틴은 아래와 같이 단일 쓰레드로 매 프레임마다 코루틴을 체크합니다. 시간으로 조건을 주었으니 매 프레임마다 흐른 시간을 체크한다고 생각하시면 되겠습니다.
- 이는 지연실행의 개념으로 '1' frame 체크를 한 뒤에 호출이 되겠습니다. (1프레임이 지남, 조건이 충족되었는지 확인)
- 디버깅을 통해 이를 확인할 수 있습니다. (디테일하게 체크를 하면 원하는 타이밍보다 한 템포 늦게 확인이 됩니다.)
- 반면 쓰레드는 아래와 같이 각각 프로세스가 따로 돌아가면서 일 처리를 합니다.
쓰레드는 어떤 프로세스가 먼저 실행되고 종료될지는 알 수가 없습니다.(순서 보장되지 않음) CPU성능에 맞춰 적당히 분배해서 실행해준다고 생각하시면 됩니다.
마무리
예전에 이해는 했지만 설명을 제대로하지 못 해서, 따끔하게 혼났던 기억이 있네요. 머리로는 이해했지만 설명을 제대로 하지 못 한다면 그 역시 모르는 것과 같다고 하셨던 한마디가 아직도 기억에 남습니다.
유니티는 그렇다고 싱글 쓰레드만 사용하는 것일까요?
아닙니다. 랜더링, 여러 업데이트들 호출하기 위한 부모 로직, 코루틴, 인보크 등 여러가지가 돌아가기 위해서 백그라운드 워커 쓰레드가 실행 중이며, 프로파일러를 통해 확인이 가능합니다. 단일 쓰레드만 사용한다는 전설은 어디서 나온걸까요?
'Study' 카테고리의 다른 글
C#, 우선순위 큐 개념과 힙을 통한 구현 (1) | 2022.07.10 |
---|---|
C#, Enum 'Flags' Technic (0) | 2022.02.28 |
HTTP 통신과 TCP 통신의 차이와 이해 (0) | 2022.02.10 |
C#, 매개 변수 한정자 out과 ref 기능 및 class 사용 이유 (0) | 2022.02.09 |
C#, 쓰레드 동기화를 위한 뮤텍스와 셰마포어 예제 (0) | 2022.02.02 |
댓글