코루틴은 유니티 뿐만 아니라 다른 프로그맹이에서도 등장하는 유용한 요소이기에 꼭 알아야하는 기능입니다.
과거 코루틴을 처음 접할때는 간단하게 무언가 기다릴 때 사용한다고만 생각했는데 자세히 알아보니 중요한 기능였네요,,
그러므로 쉬운 것 같으면서도 헷갈리는 코루틴을 공부 해보도록 하겠습니다.
목차
- 코루틴이란?
- 코루틴 실행 방식
- IEnumerator 구조
- 핵심
1. 코루틴이란?
코루틴은 Co(함께) + routine(규칙적인 일의 순서)가 합쳐진 단어입니다.
일반적인 함수와는 달리 코루틴은 중지 - 재개할 수 있으며 여러프레임에 걸쳐서 실행되고 시간지연 같은 작업 등 유용하게 사용할 수 있습니다.
중지 : Unity에게 제어권을 돌려줍니다. ( 제어권: 스크립트 실행, 렌더링 작업 등의 수행 )
재개 : Unity에게 제어권을 돌려 받고 중지된 위치에서 이어서 실행합니다.
- 중지된 위치는
yield return
이 약속한 시간동안 기다립니다. ( yield : 양보 )
- 중지된 위치는
2. 코루틴 실행 방식
코루틴은 사용하다보면 스레드가 생성되는 멀티스레드라 오해하실 수 있습니다.
하지만 코루틴은 싱글스레드로 비동기 방식을 구현합니다. ( 비동기 : 동시에 일어나지 않음. )
[사전 지식] 프로세스(Process)
- 프로세스는 프로그램이 사용할 메모리 공간을 할당받아 실행 중인 것을 말합니다.
[사전 지식] 스레드(Thread)
스레드란 프로세스 내에서 실제로 작업을 수행하는 주체를 의미합니다.
모든 프로세스에는 한 개 이상의 스레드가 존재합니다.
두 개 이상의 스레드를 가지고 있는 프로세스를 멀티스레드 프로세스라고 합니다.
멀티 스레딩의 단점
멀티스레딩은 컨텍스트 스위칭, 동기화 문제, 데드락 등 복잡한 문제(버그)를 가지고 있고 스레드를 생성하고 제거하는 작업에 상당한 오버헤드를 가진다고 합니다.
반면 코루틴은 싱글스레드 내에서 여러 작업을 동시에 실행하는 것처럼 보이게 만듭니다.
이러한 이유는 앞에서 말했듯이 Unity와 제어권을 돌려가며 사용하기 때문에 동시성을 구현하기 때문입니다. 이 때문에 멀티스레드로 오해하는 경우가 생깁니다.
코드로 이해하기
구글링하며 공부하다가 블로그에서 좋은 코드 예시를 발견했습니다. 아마 보시면 직관적이고 이해하기 쉬울겁니다.
다음 코드는 Start
함수에서 코루틴 -> 일반함수 순서로 호출하는 과정입니다.
코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NewBehaviourScript : MonoBehaviour
{
void Start()
{
StartCoroutine(countCo());
countCommon();
}
// 0부터 4까지 증가하는 일반 함수
void countCommon()
{
for (int i = 0; i < 5; i++)
{
Debug.Log("일반 함수: " + i);
}
}
// 0부터 4까지 증가하는 코루틴
IEnumerator countCo()
{
for (int i = 0; i < 5; i++)
{
Debug.Log("코루틴: " + i);
yield return null;
}
}
}
결과
아마 멀티스레딩이라면 디버그가 번갈아가듯 출력할 겁니다.
하지만 코루틴은 싱글스레드이기 때문에 for 상황에서도 디버그 출력 후 1프레임을 기다리고 1프레임 동안 countCommon()의 for이 끝나고 돌아오는 것을 확인할 수 있습니다.
3. IEnumerator 구조
C#의 IEnumerator
IEnumerator는 C#의 인터페이스 중 하나로, 컬렉션을 순차적으로 접근하는 데 사용됩니다.
IEnumerator 인터페이스
MoveNext()
- 컬렉션의 다음 요소로 이동합니다. 만약 다음 요소가 존재하면 true를 반환하고, 그렇지 않으면 false를 반환합니다. 즉, 컬렉션의 모든 요소를 순회했는지 아닌지를 나타냅니다.
Reset()
- 열거자를 컬렉션의 시작 위치, 즉 첫 번째 요소 이전의 위치로 재설정합니다. ( 일반적으로 잘 사용 안함 )
Current
이 프로퍼티는 컬렉션에서 열거자의 현재 위치에 있는 요소를 반환합니다.
예외 상황:
- MoveNext() 실행 전일 때
- MoveNext()가 false를 반환할 때
- Reset() 호출된 후
Unity의 코루틴 IEnumerator
Unity의 코루턴에서 IEnumerator는 조금 다르게 사용됩니다.
MoveNext()는 yield return을 만날 때마다 호출되며, 다음 실행할 코드가 있으면 true 아니라면 false 반환합니다.
Current 프로퍼티는 yield return의 값을 반환합니다.
- 예: null, WaitForSeconds 등..
예시로 이해하기 위해 다음 코드를 확인합시다.
1
2
3
4
5
6
7
8
9
10
IEnumerator TestCoroutine()
{
Debug.Log("코루틴 시작");
yield return null; // MoveNext(): true, Current: null
Debug.Log("코루틴 끝");
yield return null; // MoveNext(): false, Current: MoveNext()가 false이므로 사용x
}
C#의 IEnumerator와 Unity 코루틴의 IEnumerator 차이
C#은 컬렉션의 다음 요소가 존재하는지 확인하고, Unity의 코루틴은 함수안에서 다음 코드가 진행되는지를 확인합니다.
컬렉션: 데이터의 검색과 저장에 특화된 기능입니다. (자료구조임!)
4. 핵심
코루틴은 싱글스레드로 비동기성을 구현한다.
코루틴은 멀티스레드인 것처럼 동시성을 구현하는 것 같지만 실제로는 Unity와 제어권을 돌려가며 동시성인 것처럼 보이는 것이다.
Unity의 코루틴에서 IEnumerator 구조
yield return이 실행되면 MoveNext()가 호출되고 다음 코드가 있으면 true 없으면 false를 반환한다.
yield return의 반환값은 Current가 가진다. ( null, WaitForSeconds, … )