[Unity] can only be called from the main thread. 문제 해결하기
카테고리: C# + Unity
Unity 엔진의 동작 원리
Unity 엔진은 많은 블로그와 Unity 공식 문서에서 아래의 그림과 같이 Main Thread와 Render Thread, Worker Thread들로 구성되어 있다고 설명한다.
위와 같은 구조로 인해서 UI 요소를 변경하는 함수들은 메인 스레드 위에서만 작동한다. 이로 인해서 멀티 스레드 환경에서 메인 스레드가 아닌 다른 스레드가 UI 요소를 변경하면 아래와 같은 메시지를 출력한다.
[문제가 된 함수] can only be called from the main thread.
[문제가 된 함수]는 메인 스레드에서만 호출할 수 있습니다.
문제 해결
위와 같은 문제를 C#에서는 Invoke를 사용하여 메인 스레드로 호출을 위임하지만, Unity에서는 해당 기능을 지원하지 않아 이를 직접 구현해야 한다. 멀티 스레드에서 작업하던 것을 메인 스레드에서 함수 호출을 위임해 처리해야 한다는 것을 아이디어로 대리자 패턴 및 싱글톤 패턴과 결합하여 UIMQ(User Interface Message Queue)를 구현하면 된다.
소스 코드
- UIMessageQueue.cs
using System;
using System.Collections.Generic;
public class UIMessageQueue {
private static UIMessageQueue instance;
private Queue<Action> jobs;
private UIMessageQueue() {
jobs = new Queue<Action>();
}
public static UIMessageQueue GetInstance() {
instance ??= new UIMessageQueue();
return instance;
}
public void Enqueue(Action action) {
jobs.Enqueue(action);
}
public Action Dequeue() {
return jobs.Dequeue();
}
public int JobCount() {
return jobs.Count;
}
}
- Example.cs
public class Example : MonoBehaviour {
UIMessageQueue UIMQ;
void Start() {
UIMQ = UIMessageQueue.GetInstance();
}
void Update() {
while (UIMQ.JobCount() > 0) {
UIMQ.Dequeue().Invoke();
}
}
void SetButtonActive(string buttonName, bool value) {
UIMQ.Enqueue(() => {
GameObject.Find(buttonName).SetActive(value);
});
}
}
참고
https://blog.danggun.net/10031
https://forum.unity.com/threads/what-does-this-error-mean.338592/
https://opchacha.tistory.com/21
https://sonsazang.tistory.com/25