1. 개요
코루틴(Coroutine)은 비동기 프로그래밍과 동시성(concurrency) 처리를 위한 프로그래밍 구조로, 함수나 루틴의 실행을 중단(suspend)하고 필요 시 다시 재개(resume)할 수 있는 특성을 가진다. 코루틴은 일반적인 서브루틴(subroutine)과 달리, 실행 흐름을 협력적으로 제어할 수 있으며, 이를 통해 효율적인 자원 관리와 코드 간결화를 가능하게 한다.코루틴은 다음과 같은 특징을 갖는다.
- 중단과 재개: 코루틴은 실행 중간에 특정 키워드를 사용하여 실행을 중단하고 상태를 저장한 뒤, 나중에 다시 실행을 재개할 수 있다.
- 비동기 처리: 입출력(I/O) 작업이나 긴 실행 시간을 요구하는 작업에서 비동기적으로 실행되며, 동시성을 지원한다.
- 가벼움: 코루틴은 운영체제 스레드와 달리 가벼운 단위로 동작하며, 하나의 스레드에서 다수의 코루틴을 실행할 수 있다.
- 상태 유지: 코루틴은 중단된 시점의 상태(변수, 호출 스택)를 유지하며, 재개 시 이전 상태를 복원하여 실행을 이어간다.
Coroutine이라는 용어는 co-(함께)와 routine(루틴)의 합성어로, 여러 루틴이 협력(cooperative)적으로 실행을 제어하는 프로그래밍 패턴을 의미한다. 이 개념은 1960년대에 멜빈 콘웨이(Melvin Conway)에 의해 처음 소개되었다. 코루틴은 1960년대에 등장하여 컴퓨터 과학 초기 연구에서 알고리즘 간 협력적 실행을 구현하기 위해 도입되었다. 이후 다양한 프로그래밍 언어에서 비동기 프로그래밍을 지원하는 주요 개념으로 채택되었으며, 현대에는 Kotlin, JavaScript, C#, Go 등 여러 언어에서 다양한 방식으로 구현되고 있다.
2. 스레드와의 관계
코루틴(Coroutine)과 스레드(Thread)는 모두 동시성 프로그래밍을 지원하는 개념이지만, 서로 다른 동작 방식과 설계 철학을 가지고 있다. 코루틴은 비동기적이고 협력적인 동작을 기반으로 하며, 스레드는 운영체제에서 제공하는 선점형 동시성을 기반으로 한다. 두 개념은 서로 보완적으로 사용되기도 하며, 특정 상황에서는 한쪽이 더 적합한 선택이 될 수 있다.특징 | 코루틴(Coroutine) | 스레드(Thread) |
제어 방식 | 협력적 제어 (Cooperative Scheduling) | 선점형 제어 (Preemptive Scheduling) |
실행 단위 | 애플리케이션 레벨에서 실행 흐름 관리 | 운영체제 레벨에서 실행 흐름 관리 |
컨텍스트 전환 비용 | 낮음 (스택 스위칭 필요 없음) | 높음 (스택과 레지스터 컨텍스트 전환 필요) |
병렬성 | 단일 스레드 내에서 동작 (병렬 실행 불가능) | 다중 코어를 활용하여 병렬 실행 가능 |
상태 관리 | 상태는 애플리케이션 코드에서 관리 | 상태는 운영체제가 관리 |
수량 | 한 스레드에서 수천 개의 코루틴 실행 가능 | 스레드 수는 운영체제 및 하드웨어 자원에 제한됨 |
오버헤드 | 적음 (가벼움) | 높음 (스레드 스택, 스케줄링 비용) |
코루틴과 스레드는 서로 대체되는 개념이라기보다는, 상호 보완적으로 사용될 수 있다. 아래는 그 관계를 나타낸 몇 가지 사례이다:
- 스레드 위의 코루틴
- 대부분의 코루틴 프레임워크(예: Kotlin, Python)는 코루틴이 스레드 위에서 실행되도록 설계되어 있다.
- 하나의 스레드에서 다수의 코루틴을 실행할 수 있으며, 코루틴의 비동기적 특성 덕분에 스레드 수를 줄이고 자원 사용을 최적화할 수 있다.
- 병렬성과 동시성의 결합
- 스레드는 **병렬성(parallelism)**을 구현하는 데 적합하고, 코루틴은 **동시성(concurrency)**을 구현하는 데 적합하다.
- 예를 들어, 코어가 4개인 시스템에서 4개의 스레드가 병렬로 실행되면서, 각 스레드 내에서 코루틴이 동시성을 처리하는 방식으로 결합할 수 있다.
코루틴은 스레드를 완전히 대체하지 못한다. 두 개념은 서로 다른 문제를 해결하기 위해 설계되었기 때문에, 각각의 강점과 약점을 가진다. 일반적으로 I/O 중심 작업이나 대기 시간이 많은 작업은 코루틴이 더 적합하며, CPU 중심 작업이나 병렬 처리는 스레드가 더 적합하다.
코루틴과 스레드는 동시성과 병렬성의 두 축을 담당하며, 각기 다른 요구사항에 적합한 솔루션을 제공한다. 코루틴은 비동기와 가벼운 실행을, 스레드는 병렬성과 하드웨어 활용을 중점적으로 지원한다. 현대 프로그래밍에서는 두 개념을 상호 보완적으로 활용하여 높은 성능과 효율성을 달성할 수 있다.
3. 코루틴을 지원하는 언어
- C++: C++20에서 stackless coroutine 기능이 추가되었다. 이를 통해 co_await, co_yield, co_return과 같은 키워드를 사용하여 비동기와 비선형 흐름을 구현할 수 있다.
- Rust: 2018 에디션에서 코루틴 관련 문법과 트레이트(Trait)를 지원한다. 하지만 실행자를 직접 구현하거나, async-std, tokio와 같은 라이브러리를 사용해야 한다.
- Go: 언어 차원에서 고루틴(goroutine)이라는 기능을 제공한다. 고루틴은 기존 스레드보다 가볍고 빠르며, 여러 스레드에서 코루틴을 병렬적으로 실행할 수 있다. 이는 운영체제가 제공하는 스레드보다 경량화된 가상 스레드 개념이다.
- 자바스크립트: ES6 이전에는 AJAX와 콜백(callback)을 사용하여 비동기를 구현했다. ES6부터는 Promise 객체가 등장했으며, async와 await 키워드가 추가되어 코루틴과 유사한 비동기 프로그래밍이 가능해졌다.
- PHP: PHP 5.5부터 제너레이터(generator)를 기반으로 코루틴을 지원한다. yield 키워드를 사용하여 실행을 중단하고 재개할 수 있다.
- C#: C# 2.0에서 yield 키워드를 통해 코루틴을 지원하기 시작했다. 이후 C# 5.0에서 async와 await 키워드가 추가되며 본격적으로 비동기 프로그래밍을 강화했다.
- 코틀린: Kotlin 1.3부터 코루틴을 언어 차원에서 지원하며, suspend, launch, async 키워드를 통해 직관적이고 강력한 비동기 코드를 작성할 수 있다.
- 파이썬: Python 3.5부터 asyncio 라이브러리를 통해 코루틴을 지원한다. async와 await 키워드를 사용하여 비동기 처리를 구현할 수 있다.
- 루아: Lua에서는 코루틴(coroutine)이 언어의 기본 타입 중 하나로 제공된다. Lua에서는 coroutine.create와 coroutine.resume 같은 함수를 사용하여 코루틴을 다룰 수 있으며, 이를 thread 타입으로 명명한다.