// 1. 비동기 요청 mtd . Request (() => res1 = Random . Range ( 0 , 1000 )); Debug . Log ( res1 ); // 2. await를 통한 대기 - Action await mtd . RequestAsync (() => { res2 = Random . Range ( 0 , 1000 ); }); Debug . Log ( res2 ); // 3. await를 통한 대기 - Func<int> Task < int > resultTask = mtd . RequestAsync (() => Random . Range ( 0 , 1000 )); await resultTask ; Debug . Log ( resultTask . Result ); Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. https://github.com/PimDeWitte/UnityMainThreadDispatcher // 날짜 : 2021-06-30 AM 2:56:52 using UnityEngine ; using System ; using System.Collections ; using System.Collections.Generic ; using System.Threading ; using System.Threading.Tasks ; using MTD = MainThreadDispatcher ; public class MainThreadDispatcher : MonoBehaviour /*********************************************************************** * Singleton ***********************************************************************/ # region . private static MTD _instance ; public static MTD Instance if ( _instance == null ) _instance = FindObjectOfType < MTD >(); if ( _instance == null ) GameObject container = new GameObject ( $"Main Thread Dispatcher" ); _instance = container . AddComponent < MTD >(); return _instance ; private void Awake () if ( _instance == null ) _instance = this ; transform . SetParent ( null ); DontDestroyOnLoad ( this ); if ( _instance != this ) if ( GetComponents < Component >(). Length <= 2 ) Destroy ( gameObject ); Destroy ( this ); # endregion private static readonly Queue < Action > _executionQueue = new Queue < Action >(); private void Update () lock ( _executionQueue ) while ( _executionQueue . Count > 0 ) _executionQueue . Dequeue (). Invoke (); /// <summary> 메인 스레드에 작업 요청(코루틴) </summary> public void Request ( IEnumerator coroutine ) lock ( _executionQueue ) _executionQueue . Enqueue (() => StartCoroutine ( coroutine ); /// <summary> 메인 스레드에 작업 요청(메소드) </summary> public void Request ( Action action ) Request ( ActionWrapper ( action )); /// <summary> 메인 스레드에 작업 요청 및 대기(await) </summary> public Task RequestAsync ( Action action ) var tcs = new TaskCompletionSource < bool >(); void WrappedAction () action (); tcs . TrySetResult ( true ); catch ( Exception ex ) tcs . TrySetException ( ex ); Request ( ActionWrapper ( WrappedAction )); return tcs . Task ; /// <summary> 메인 스레드에 작업 요청 및 대기(await) + 값 받아오기 </summary> public Task < T > RequestAsync < T >( Func < T > action ) var tcs = new TaskCompletionSource < T >(); void WrappedAction () var result = action (); tcs . TrySetResult ( result ); catch ( Exception ex ) tcs . TrySetException ( ex ); Request ( ActionWrapper ( WrappedAction )); return tcs . Task ; /// <summary> Action을 코루틴으로 래핑 </summary> private IEnumerator ActionWrapper ( Action a ) a (); yield return null ; 메소드로만 사용하던 GetComponent(), Find() 기능들을 필드/프로퍼티 애트리뷰트로 간편하게 사용할 수 있다. 리플렉션과 커스텀 애트리뷰트를 활용하여 제작하였다. Component를 상속받는 타입의 필드/프로퍼티에 사용할 수 있다. 대상 멤버의 접근지정자에 관계 없이 모두 동작한다. 본 애트리...