ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 코루틴 (in Unity)
    Unity 2023. 10. 6. 02:24

    I. 용어의 이해

     

     프로세스 (process)

     

        : 프로그램이 운영체제에 의해 메모리 공간을 할당받아 실행 중인 것.

        : 자원 (데이터 / 메모리 / ... etc) 과 스레드로 구성됨.

     

    스레드 (thread)

     

       : 프로세스 내에서 실제로 독립적인 작업을 수행하는 주체

       : 주체가 많은 경우 (멀티 스레드 - multi thread) 한번에 여러 일을 처리 (각 스레드가 각자 다른 작업을 수행) 할 수 있다.

       : 여러 작업을 병렬(parallelism)로 처리하는 것은 스레드와 직접적인 관계 없다.

     

    병렬성 (parallelism)

     

        :  여러 작업을 동시에 실행하는 방법

        :  병렬 처리는  오래 걸리는 작업을 기준으로 실행 시간이 결정된다.

     

        :  아래의 그림에서 파랑차와 초록차 중 더 느린 차의 작업이 끝나야 병렬 처리가 끝나는 것.

     

     

     동시성 (concurrency)

     

        : 병렬 처리를 시뮬레이션하여 여러 작업이 동시에 진행되는 것 처럼(병렬로 처리되는 것 처럼) 보이게 하는 성질

           

           - 동시성을 구현하기 위해 멀티스레드를 동작시켜 여러 작업간의 전환(context-switching 프로세스)을 통해 작업을 처리함.

             작업간의 전환을 통해 진행하기 때문에, 작업간의 상태 공유, 동기화, 작업간 통신 등이 필요.

     

           - 병렬성은 여러 작업이 동시에 실행되는 개념임.

              작업간의 독립성을 강조하며, 작업간 영향을 최소화함.

     

     

     비동기성 (asynchrony)

     

        : 프로그램의 주 실행 흐름을 멈추어서 기다리는 부분 없이 다른 작업을 실행할 수 있게 하는 방식

        : 작업의 처리를 별도의 공간에 맡겨둔 후, 결과를 기다리지 않고 다음 코드를 실행한다.

     

     

    <정리>

     

    II. 코루틴

     

      : 비동기적으로 실행되는 함수.

      : 실행을 일시 정지하고 제어를 Unity에 반환하지만 중단한 부분에서 다음 프레임을 계속할 수 있게 하는 메서드 (Unity)

      : 코루틴은 Ienumerator 반환 타입과 바디 어딘가에 포함된 yield 반환문으로 선언하는 메서드

     

     

     1. Unity 에서의 코루틴은 Update() 메서드에서 프레임과 프레임 사이에서 실행된다.

     

    코루틴과 Update()로 같은 기능을 구현할 수 있다.

     

     !! 2가지 종류 (3초, 5초) 의 타이머를 Update()에서 구현

    public class CoroutineExample : MonoBehaviour
    {
        float counterForFirst;
        float counterForSecond;
    
        void Update()
        {
            CallPerSecond(5.0f, ref counterForFirst);
            CallPerSecond(3.0f, ref counterForSecond);
        }
    
        void CallPerSecond(float second, ref float counter)
        {
            counter += Time.deltaTime;
            if (counter >= second)
            {
                Debug.Log($"{second}초가 지났습니다.");
                counter = 0f;
            }
        }
    }

    >

     

    !! 2가지 종류 (3초, 5초) 의 타이머를  코루틴을 통해 구현

    public class CoroutineExample : MonoBehaviour
    {
        bool isFirstDelayed = false;
        bool isSecondDelayed = false;
    
        void Update()
        {
            if (!isFirstDelayed)
            {
                isFirstDelayed = true;
                StartCoroutine(CountTime(3f));
            }
            if (!isSecondDelayed) 
            {
                isSecondDelayed = true;
                StartCoroutine(CountSecondTime(5f));
            }
        }
    
        IEnumerator CountTime(float second)
        {
            yield return new WaitForSeconds(second);
            Debug.Log($"{second}초가 지났습니다.");
            isFirstDelayed = false;
        }
    
        IEnumerator CountSecondTime(float second)
        {
            yield return new WaitForSeconds(second);
            Debug.Log($"{second}초가 지났습니다.");
            isSecondDelayed = false;
        }
    }

    >

     

    실행 결과는 같으나,

     

    Update() : 매 프레임 (고정된 일정 주기)마다 작업을 수행.

    코루틴 : yield를 통해 설정한 구간까지 수행을 멈추며, 다른 Unity의 이벤트 함수에 제어권을 넘겨주었다가, 다시 가져옴.

     

    의 차이가 있다.

     

    정확한 타이밍에 본인이 원하는 기능을 구현해야 할 때에는 코루틴을 사용하는 것이 유리하다.

    (ex. 애니메이션간 전환)

     

    하지만, 다수의 코루틴을 사용한다면, 다수의 작업을 동시에 처리하려고 한다는 점과, 호출되고 제어권을 반환할 때마다,  새로운 인스턴스를 생성해서 사용하여 가비지를 생성하기 때문에 성능 저하 이슈를 야기할 수 있다.

     

    비교적 단순하고, 자원이 많이 들지 않은 기능의 구현은 Update() 메서드를 통해 구현하는 것이 좋다.

     

    따라서, 상황과 필요에 따라, 어느 하나만 사용하기 보다는 둘 다 적합한 장소에서 구현하는 것이 좋겠다.

     

     

      2.  Unity 에서의 코루틴은 단일 스레드로 동작한다.

     

         = 뿐만 아니라, 유니티에서 제공하는 기능들 (ex. 게임 오브젝트) 은 메인 스레드에서만 실행되어야 한다.

         = C# 에서 제공하는 멀티 스레드를 통해 구현한 기능들 (ex. 통신, 파일 로딩, DB 접근) 등은 Unity 내 라이브러리를 통해 메인 스레드에서 실행토록 함.

     

         - Unity 에서 메인 스레드에서만 실행되기 때문에, 비동기 작업을 처리하면서, 게임 루프의 제어를 유지할 수 있다.

     

     

     

      3.  코루틴 문법 요소

     

     StartCoroutine()

     

     : 파라미터의 코루틴 호출

     

    StartCoroutine("코루틴_이름");
    StartCoroutine(코루틴_이름());

     

     StopCoroutine()

     

      : 파라미터의 코루틴 중지

     

    StopCoroutine("코루틴_이름");
    StopCoroutine(코루틴_이름());

     

     IEnumerator 인터페이스 (Unity)

     

        : 코루틴을 구현하는데 사용되는 인터페이스

     

    IEnumerator 코루틴_이름()
    {
    	// ... 기능 구현 (생략) ...
        
    	yield return 반환_방식
    }

     

          : 반드시 하나 이상의 yield 반환문을 포함해야 한다.

     

          : 반환하는 즉시 메서드가 종료되는 return 과 다르게, yield return은 IEnumerator 가 다시 호출될 때마다, 이전에 권한을 이벤트 함수에 제어권을 넘겨준 시점으로 돌아와 코드를 실행한다.

     

    public class CoroutineExample : MonoBehaviour
    {
        private void Start()
        {
            callCountType();
        }
        
        void callCountType()
        {
            IEnumerator getTypeEnum = GetCountTypeIEnumerator();
            getTypeEnum.MoveNext();
            Debug.Log(getTypeEnum.Current);
            getTypeEnum.MoveNext();
            Debug.Log(getTypeEnum.Current);
            getTypeEnum.MoveNext();
            Debug.Log(getTypeEnum.Current);
            getTypeEnum.MoveNext();
            Debug.Log(getTypeEnum.Current);
        }
    
        IEnumerator GetCountTypeIEnumerator()
        {
            int a = 3;
            Debug.Log($"TypeA : {a}");
            yield return a;
            int b = 5;
            Debug.Log($"TypeB : {b}");
            yield return b;
            int c = 4;
            Debug.Log($"TypeC : {c}");
            yield return c;
        }
    }

    >

     

        1.  IEnumerator 에서 첫 yield return 까지 실행된 후, 제어권을 넘김.

     

     

        2. getTypeEnum.Current를 통해 현재 위치(yield return a)의 값을 가져옴.

     

     

         3. getTypeEnum.MoveNext() 를 통해 이전에 제어권을 넘긴 시점 (yield return a)의 다음 위치부터 코드 실행

     

     

         의 흐름으로, 순차적인 작업을 한 곳에 두고, 각 작업을 각기 원하는 시점에 실행할 수 있음을 알 수 있다.

     

     IEnumerator 인터페이스 (C#)

     

      : 제네릭이 아닌 컬렉션을 단순하게 반복할 수 있도록 지원함.

      : 유니티의 IEnumerator 와 다르다..

     

    public interface IEnumerator
    {
        object Current { get; }
        bool MoveNext();
        void Reset();
    }

     

         A. 속성 

          - Current

            : 컬렉션에서 열가자의 현재 위치에 있는 요소를 가져옴

     

         B. 메서드

           - MoveNext()

             : 열거자를 컬렉션의 다음 요소로 이동함.

             : 다음 위치의 데이터의 유무에 따라 bool 값이 정해진다. => 해당 index가 컬렉션의 끝 요소인가?

     

           - Reset()

             : 컬렉션의 첫 번째 요소 앞의 초기 위치에 열거자를 설정함.

             : 선택적으로 구현할 수 있는 메서드 (대부분의 경우 메서드를 구현할 필요 없음)

             = C# 8.0 이상부터 부분 메서드를 사용하여 일부 메서드를 구현하지 않고도 인터페이스를 구현할 수 있음.

     

    IEnumerator<string> enumerator = someCollection.GetEnumerator();
    while (enumerator.MoveNext())
    {
        string item = enumerator.Current;
    }

     

     yield return

     

       : 반환

     

         A. yield return null

     

            : 한 프레임동안 제어권을 넘김

     

         B. yield return new WaitForSeconds(float second)

     

            : second 의 초만큼 제어권을 넘김

     

          C. yield return new WaitForSecondsRealtime(float second)

     

             : Time.timeScale의 영향을 받지 않는 절대적인 시간으로 second 의 초만큼 제어권을 넘김

     

           D. yield break

     

              : 코루틴 탈출 (정지)

     

           E. yield return WaitForEndOfFrame()

     

               : 한 프레임워크가 완전히 종료될 때까지 제어권을 넘김.

               : 모든 Update() 및 렌더링이 끝난 이후

     

            F. yield return new WaitUntil(람다식 조건)

     

                : 람다식 조건이 true가 될 때까지 제어권을 넘김

     

     

    'Unity' 카테고리의 다른 글

    네트워크 게임의 이해  (0) 2023.10.24
    readme 작성  (0) 2023.10.19
    UI와 canvas  (0) 2023.09.11
    마을 만들기 기획 및 학습계획  (0) 2023.09.05
    Unity 용어 및 인터페이스  (0) 2023.09.05
Designed by Tistory.