[Unity] 유니티는 왜 싱글 쓰레딩을 권장하는가?

2024. 11. 28. 19:07UNITY/공부

728x90
반응형

동기 비동기 관련된 개념만 보면 비동기식 멀티 쓰레딩이 가장 좋아보인다. 하지만  유니티는 우리가 알다시피 싱글 쓰레딩을 권장한다. 왜 그러는지 의문점이 생겨 작성한 글이다.

비동기식 멀티 쓰레딩 개념에 대해 정리 해놓은 부분을 다시 읽어보면 다음고 같이 적혀있다.

나는 사장이고 베이컨을 굽는 직원이 있고 토스트를 굽는 직원이 있고 서빙을 담당하는 직원이 있다고 보면 된다.

베이컨 주문이 10개 들어오면 팬에 베이컨을 올리고 타이머를 누르는 작업을 10번 수행하고 10개의 타이머가 순차적으로 울릴면 차례로 뒤집는 작업을 한다. 토스트를 굽는 직원도 동일한 메커니즘이다.

이는 이상적인 식당의 모습이다. 그러나 실제 식당의 모습은 위처럼 흘러가지만은 않는다. 이를테면 다음과 같은 문제들이 발생할 수 있다.

 

1. 작업 충돌 : 같은 팬을 두고 싸우는 직원들

문제 상황:

베이컨 직원과 토스트 직원이 같은 팬을 사용하는 상황이라고 가정해보자. 베이컨 직원이 팬에 베이컨을 올리려는 순간, 토스트직원이 같은 팬에 토스트를 굽고자 한다. 둘 다 팬을 사용해야 하기 때문에 경합(race condition)이 발생한다.

현실 문제:

멀티스레딩에서 여러 스레드가 공유 자원(shared resource)에 동시에 접근하면 데이터가 꼬일 위험이 있다. 이를 방지하기 위해 락(Lock) 같은 동기화 도구를 사용해야 하지만, 이는 작업 속도를 늦추고 복잡성을 증가시킨다.


2. 데이터 손실 : 데이터 설명을 잊은 직원

문제 상황:

베이컨 직원이 팬에 베이컨을 올리고 타이머를 설정하지 않았다. 직원이 다른 작업으로 바쁘던 중 타이머 없이 구워진 베이컨은 결국 타버린다. 고객은 "왜 베이컨이 까맣게 탔나요?" 라고 항의한다.

현실 문제:

비동기 작업에서 예외 처리가 제대로 되지 않으면 작업 상태가 유실될 수 있다. 예를 들어, 네트워크 요청이 실패했는데 이를 감지하지 못하거나, 비동기 작업중간에 예외가 발생해 프로그램이 예상치 못한 동작을 할 수 있다.


3. 교착 상태(Deadlock) : 서로 기다리는 직원들

문제 상황:

베이컨 직원이 토스트를, 토스트 직원이 베이컨을 올려야 한다고 착각해 서로의 작업이 끝나기를 기다린다. 이 상태에서는 아무 일도 일어나지 않으며, 손님이 음식을 받지 못한다.

현실 문제:

멀티스레드 환경에서 두 개 이상의 스레드가 서로의 작업 완료를 기다리며 무한한 대기하는 상황이 발생할 수 있다. 이를 교착상태라고 하며, 시스템 동작이 완전히 멈출 수 있다.


4. 오버헤드 : 너무 많은 직원 고용

문제 상황:

베이컨 주문이 10개 들어왔는데, 사장이 10명의 베이컨 직원을 동시에 고용한다. 각각 직원이 팬을 하나씩 차지하며 타이머를 누른다. 문제는 팬이 10개 이상 필요하거나, 직원간 커뮤니케이션 비용이 발생하면 전체 작업이 더 느려질 수 있다.

현실 문제:

멀티스레드에서는 너무 많은 스레드를 생성하면 컨텍스트 스위칭(context switching)으로 인해 오히려 성능이 저하될 수 있다. cpu가 각 스레드간 작업 전환에 시간을 소비하기 때문이다.


위와 같은 이유로 비동기식 멀티쓰레딩은 효율적이고 유용할 수 있지만, 잘못 설계하면 예상치 못한 문제를 초래할 수 있다. 

 

[게임 개발에서 싱글 스레드]

게임은 프레임 단위로 동작한다. 즉, 게임은 일정한 주기로 물리연산, 업데이트, 렌더링등의 작업을 반복한다.이 과정에서 하나의 스레드에서 순차적으로 처리되면 다음과 같은 장점들을 얻을 수 있다.

1. 일관성 유지

게임 상태는 모든 프레임에서 일관성을 유지해야한다. 싱글스레드에서는 특정 시점에 실행되는 코드가 항상 동일한 순서와 상태에서 동작하므로 디버깅과 테스트가 용이하다.

2. 간결한 개발

멀티 스레드로 게임을 작성하면 스레드 관리, 동기화 문제, 교착상태 등을 개발자가 직접 처리해야 한다. 유니티의 싱글 스레드 환경에서는 이런 복잡한 문제를 피하고 게임 로직 구현에 집중할 수 있다.

3. 렌더링과 물리 연산의 특수성

유니티는 렌더링과 물리 계산을 GPU와 물리엔진에 위임한다. GPU와 물리 연산은 별도의 스레드에서 병렬 처리되므로, 게임 로직은 굳이 멀티 스레드로 처리할 필요가 없다. 

 

그렇다고 해서 유니티가 싱글 스레드만 지원하는가 그건 아니다. 유니티에서도 멀티 스레드를 사용할 수 있다.

  • UnityWebRequest: 네트워크 요청을 비동기로 처리.
  • Resources.LoadAsync: 리소스 로딩을 비동기로 처리.
  • SceneManager.LoadSceneAsync: 씬 로딩을 비동기로 처리.
IEnumerator LoadScene()
{
    AsyncOperation asyncLoad = SceneManager.LoadSceneAsync("MyScene");
    while (!asyncLoad.isDone)
    {
        yield return null; // 프레임마다 대기
    }
    Debug.Log("씬 로딩 완료");
}

 

결론

유니티가 싱글 스레드를 기본적으로 권장하는 이유는 동기화 문제를 최소화하고 개발을 간소화하기 위함이다. 그러나 특정 작업(네트워크 통신, 대규모 계산 등)은 멀티 스레드나 비동기 처리가 필요하다. '게임 로직은 싱글 스레드에서 무거운 작업은 비동기적'으로 라는 철학을 바탕으로 적절한 벙렬 처리를 설계하는 것이 중요해 보인다.

 

728x90
반응형