본문 바로가기
Study/Unity

[Unity] Unity Main Thread 처리

by 스테디코디스트 2025. 9. 2.
반응형
Unity에서 외부 Thread를 만들어 사용할 때 유니티 관련 처리는 유니티 Main Thread에서 처리해야한다.

 

- 유니티 내에서 외부 Thread를 사용해 TCP/UDP 서버가 동작하도록 만들었는데, 내부에서 유니티 관련 함수를 하니 빌드해서 테스트 했을 때 간혹 빌드본이 응답없음이 되면서 잘못된 주소를 참조했습니다.라는 오류가 떴다. 그래서 알아보니 유니티에서 외부 Thread를 사용할 때, 유니티의 GameObject나 Transform을 변경하거나 GetComponent 등의 함수를 호출했을 때 그런 문제가 발생할 수 있다고 한다.

 

- 그래서 이런 문제를 해결하기 위해 해당 함수를 유니티 Main Thread로 가져와 사용하는 방식을 사용해서 해결했고, 그 방식은 아래와 같이 외부에서 사용되는 함수를 Action에 담아 큐에 넣어두고 유니티 Update 함수에서 순차적으로 큐에서 뽑아서 사용했다.

예시 코드
    // 메인스레드 처리 큐
    private readonly Queue<Action> _mainThreadActions = new Queue<Action>();
    
    void Update() 
    {
        // 실행해야될 함수 배열
        Action[] actionsCopy;
        
        // lock을 이용해 다른 Thread에서 큐에 추가할 때 호출되지 않도록 충돌 방지
        // -> Enqueue와 같은 lock을 사용
        lock (_mainThreadActions)
        {
            // 받아온 모든 함수를 복사
            actionsCopy = _mainThreadActions.ToArray();
            
            // 기존 큐는 clear
            _mainThreadActions.Clear();
        }

        // 복사한 배열을 돌면서 순차적으로 실행함
        foreach (var action in actionsCopy)
        {
            try
            {
                // null을 방지하면서 안전하게 실행
                action?.Invoke();
            }
            catch (Exception e)
            {
                Debug.LogError("Unity Main Thread 실행 중 오류 발생");
            }
        }
    }
    
    /// <summary>
    /// Unity Main Thread에서 사용되어야 할 함수를 Main Thread 처리 큐에 넣어주는 함수
    /// </summary>
    /// <param name="action"></param>
    public void MainThreadEnqueue(Action action)
    {
        // lock을 이용해 다른 Thread에서 큐에 추가할 때 호출되지 않도록 충돌 방지
        // -> Update의 큐 복사할 때와 같은 lock을 사용
        lock (_mainThreadActions)
        {
            // 큐 오버플로우 방지
            if (_mainThreadActions.Count > 50)
            {
                Debug.LogWarning("MainThread 큐 오버플로우, 오래된 명령 버림");
                _mainThreadActions.Dequeue();
            }
            
            // Main Thread 처리 큐에 현재 함수를 넣음
            _mainThreadActions.Enqueue(action);
        }
    }

 

List를 이용한 변형 구조(GC 성능 개선) - Chat GPT 질문
using System;
using System.Collections.Generic;
using UnityEngine;

public class MainThreadDispatcher : MonoBehaviour
{
    // 대기 중인 작업 (외부 스레드에서 들어옴)
    private readonly List<Action> _pendingActions = new List<Action>();
    
    // 실행할 작업 (Update에서 실행)
    private readonly List<Action> _currentActions = new List<Action>();
    
    // 리스트 보호용 lock
    private readonly object _lockObj = new object();

    void Update()
    {
        // 스왑을 통해 복사 없이 안전하게 리스트 교체
        lock (_lockObj)
        {
            var temp = _currentActions;
            _currentActions.Clear();
            _currentActions.AddRange(_pendingActions);
            _pendingActions.Clear();
        }

        // 스왑된 리스트 실행
        foreach (var action in _currentActions)
        {
            try
            {
                action?.Invoke();
            }
            catch (Exception e)
            {
                Debug.LogError($"Unity Main Thread 실행 중 오류 발생 : {e.Message}");
            }
        }
    }

    /// <summary>
    /// 외부 스레드에서 메인 스레드로 실행할 작업을 큐에 넣음
    /// </summary>
    public void Enqueue(Action action)
    {
        if (action == null) return;

        lock (_lockObj)
        {
            // 큐가 너무 커지는 걸 방지 (예: 100개 제한)
            if (_pendingActions.Count > 100)
            {
                Debug.LogWarning("[MainThreadDispatcher] 큐 오버플로우 - 새 작업 무시됨");
                return;
            }

            _pendingActions.Add(action);
        }
    }
}