코루틴이란?

목차
대부분의 프로그램은 멀티태스킹이 필수적이다. 예를 들어 모바일 앱은 사용자와의 상호작용이 끊기지 않으면서 데이터를 주고받고 처리하는 등의 동작을 수행한다. 서버 프로그램도 수많은 요청과 데이터 처리를 하기 위해 다양한 방법을 사용한다. 만약 프로그램이 멀티태스킹을 하지 못한다면 프로그램은 지금보다 문제 해결 능력이 훨씬 뒤떨어졌을 것이다.
개발자들은 이 문제를 해결하기 위해 여러 가지 시도를 해왔고 다양한 방법이 등장했다. 코루틴(Coroutine)은 그 시도 중 하나이다. 코루틴은 동시성 프로그래밍(Concurrency Programming)1을 더 쉽게 해결할 수 있도록 만들어주었다. 초기에는 제한적으로 도입되었던 코루틴은, 최근에 들어서 Kotlin, C#, Javascript, Python, Go 등 다양한 프로그래밍 언어에서 도입되었고 동시성 프로그래밍을 쉽게 만들어 주었다.
루틴과 코루틴 #
코루틴(Coroutine)은 협동, 협력을 의미하는 영어 접두사 Co와 프로그래밍 개념 중 하나인 루틴(Routine)의 합성어이다. 루틴은 프로그래밍에서 특정 작업을 수행하는 하나의 단위를 의미한다.
프로그램에서 호출되어서 실행될 수 있는 코드 블록은 모두 루틴이라고 할 수 있으며 이 중에는 서브루틴(Subroutine)이라고 부르는 개념이 존재한다. 예를 들어 대부분의 프로그래밍 언어에서 함수(Function)은 서브루틴의 한 종류라고 할 수 있다. 서브루틴은 대표적으로 아래와 같은 특징이 있다.
- 단일 진입/종료점: 하나의 시작 시점이 존재하고 종료 시 시작 시점으로 돌아오며 종료한다.
- 계층적 호출: 서브루틴이 다른 서브루틴을 호출하면 호출된 서브루틴이 종료 될 때 까지 호출한 서브루틴은 멈추어 기다린다.
- 비자율적 동작 권한: 서브루틴은 스스로 자신을 멈추고 다른 서브루틴에게 동작 권한을 넘겨줄 수 없다.
코루틴은 루틴의 한 종류이지만 서브루틴과 차이가 존재한다. 코루틴의 특징을 정리하면 아래와 같다.
- 여러 개의 진입/종료점: 코루틴은 특정 시점에 멈추고 다른 서브루틴에게 동작 권한을 넘겨 주었다가 중단했던 지점에서 다시 재시작할 수 있다.
- 계층적이지 않은 호출: 일반적인 서브루틴과 달리, 코루틴은 작업이 종료되지 않아도 다른 코루틴에게 다시 작업 권한을 넘겨 줄 수 있다.
- 상태 저장: 코루틴은 멈췄다가 재시작하는 특징이 있기 때문에 중간에 자신의 상태를 저장해야 한다.
- 자율적 동작 권한: 코루틴은 스스로 중단하고 다른 코루틴에게 동작 권한을 넘겨 줄 수 있다.
서브루틴과 코루틴의 차이를 표로 만들어보면 아래와 같다.
특징 | 서브루틴 | 코루틴 |
---|---|---|
진입/종료점 | 단일 | 다중 |
호출 구조 | 계층적 | 비계층적 |
동작 권한 양보 | 불가능 | 가능 |
서브루틴과 달리 코루틴은 중간에 정지할 수 있고, 다시 멈춘 시점부터 재시작 할 수 있다는 특징이 있다. 이 점을 이용해 코루틴을 활용해 협력적 멀티태스킹(Cooperative Multitasking)2을 구현할 수 있다.
코루틴과 멀티태스킹 #
위의 이미지는 멀티태스킹이 아닌 방식의 동작과 멀티태스킹일 때의 동작을 간단하게 표현해 본 이미지이다. 멀티태스킹이 아닌 경우 빨간 작업이 완전히 종료되어야 파란 작업이 시작하고, 또한 파란 작업이 끝나야 초록 작업이 진행될 것이다. 반면 멀티태스킹은 빨간 작업이 진행 되다가 파란 작업을 잠깐 진행하고, 또 다른 작업을 하다가 돌아오는 방식으로 진행한다.
두 방식은 필요한 시간이 동일하다. 하지만 멀티태스킹이 아닌 경우 모든 작업이 진행될 때 까지 다른 작업은 중지된다. 만약 빨간 작업이 UI 작업인 경우라면 왼쪽의 경우 UI가 멈춘 것 처럼 보일 것이다. 하지만 멀티태스킹인 경우 UI는 틈틈히 동작하며 사용자에게 멈추지 않은 느낌을 줄 수 있다. (위 그림에서는 별 차이가 없어 보일수 있지만 작업이 충분히 작게 쪼개지면 마치 멈추지 않은 것 처럼 동작할 수 있다.)
코루틴은 멀티태스킹을 구현하는 방식 중 하나이며 그 중에서도 협력적 멀티태스킹을 구현할 수 있다. 앞서 소개한 코루틴의 동작 권한 양보를 통해 여러 개의 코루틴이 서로 동작 권한을 주고받으며 멀티태스킹을 구현한다.
코루틴과 동시성 #
실제로 여러 작업을 동시에 진행할 때에는 다양한 방법이 있다. 그 중에서 동시성(Concurrency)과 병렬성(Parallelism) 프로그래밍 방식이 존재한다.
병렬성 프로그래밍은 물리적으로 여러 개의 작업을 동시에 진행하는 방법이다. 여러 개의 CPU 코어에서 동시에 작업을 실행하거나 스레드를 여러 개 사용하는 방식으로 구현한다. 실제로 동시간에 여러 개의 작업을 하기 때문에 작업 시간이 단축되고 단위시간당 작업량이 극대화된다. 하지만 물리적인 장치가 많이 필요하고 여러 개의 분산 작업을 효율적으로 관리하는 것이 어렵다.
동시성 프로그래밍은 여러 작업을 동시에 처리하는 것처럼 보이게 하는 방법이다. 실제로 CPU 코어가 하나이더라도 작업을 번갈아 가며 처리해 구현한다. 물리적인 장치가 한정되더라도 여러 개의 작업을 처리할 수 있고, 자원을 아낄 수 있다. 하지만 병렬적 처리처럼 실제 작업 시간이 줄어들지는 않으며 오히려 스위칭 과정에서 시간이 조금 더 소모될 수도 있다.
병렬성과 동시성은 각각 장단점이 존재하고 현실에서는 필요에 따라 두 방식 모두 사용된다.
코루틴은 기본적으로 동시성 프로그래밍을 위한 방법 중 하나이다. 코루틴의 특징들은 하나의 작업 흐름 안에서 권한을 주고받으며 동작하는 것에 목적을 두고 있지 여러 개의 흐름으로 병렬적으로 나뉘어 실행하는 것에 중점을 두고 있지 않다. 하지만 실제 코루틴의 구현체들은 동시성과 함께 병렬성 프로그래밍도 할 수 있도록 구현되어 있다. 요약하자면 코루틴의 개념적 의미로는 동시성 프로그래밍을 위한 방식이지만, 현실적으로 더 효율적인 작업을 위해 병렬성 프로그래밍에서도 쓸 수 있도록 구현되어 있다.
스레드와 코루틴의 차이점 #
위에서 정리한 대로 코루틴은 동시성 프로그래밍을 위해 탄생한 개념이다. 하지만 코루틴을 사용할 이유를 찾으려면 다른 동시성 프로그래밍 방법과의 차별점을 알아야 한다.
대표적인 동시성 프로그래밍 방법 중 하나는 멀티스레딩(Multi Threading)이다. 하나의 CPU 코어에서 여러 개의 스레드(Thread)를 활용해 많은 작업을 진행할 수 있다. 멀티스레드와 코루틴 모두 동시성 프로그래밍을 구현할 수 있는 방식이지만 차이점이 꽤 많다. 아래의 표는 스레드와 코루틴을 가볍게 비교해 본 내용이다.
특징 | 스레드 | 코루틴 |
---|---|---|
관리 주체 | 운영체제 | 개발자 |
멀티태스킹 방식 | 선점형 멀티태스킹 | 협력적 멀티태스킹 |
메모리 | 스레드마다 독립적인 스택 할당 | 스택 공유 또는 작은 공간 할당 |
스레드는 운영체제가 작업의 순서를 관리한다. 따라서 어느 시점에 어떤 스레드가 실행될지 개발자가 알고 제어하기가 어렵다. 또한 운영체제의 판단 하에 관리되기 때문에 작업이 원하는 시점에 중지되기 어렵고 강제로 전환될 수 있다. 따라서 선점형 멀티태스킹 방식인 경우가 많다. (대부분의 OS는 선점형 멀티태스킹을 사용한다.) 반면 코루틴은 개발자가 작업의 순서를 관리할 수 있다. 따라서 협력적 멀티태스킹 방식이라고 할 수 있다.
모든 스레드는 운영체제로부터 고유한 스택 메모리를 할당받는다. 하지만 코루틴은 구현 방식에 따라 하나의 스레드 스택 메모리를 공유하거나 작은 공간을 할당받는다. 또한 스레드는 작업 전환(Context Switching)시 높은 비용이 발생한다. 하지만 코루틴은 원하는 시점에 작업을 전환하며 이 과정에서 스레드보다 비용이 적게 발생한다. 일반적으로 하나의 코루틴이 필요로 하는 메모리는 스레드보다 적으며 즉 스레드보다 가볍다.
또 코루틴은 그저 하나의 작업 단위인 루틴의 일종이기 때문에 하나의 스레드에서 여러 개의 코루틴이 동작하는 것도 가능하다. 즉, 여러 개의 작업을 동시성 프로그래밍으로 처리하기 위해 반드시 여러 개의 스레드를 만들 필요는 없게 된다. 다시 말해 여러 개의 스레드를 만들어 처리하던 동작도 훨신 적은 양의 스레드로 처리할 수 있게 된다.
간혹 코루틴을 경량 스레드(Lightweight Thread)라고 부르는 경우도 있다. 이는 이해를 돕는 비유로 쓰일 수도 있지만 오해를 일으키기도 한다. 코루틴은 스레드와 1:1 매칭되는 개념이 아니다. 스레드는 OS에서 관리하는 것이며 코루틴은 스레드 위에서 실행될 수 있는 코드 덩어리이다. 둘은 엄연히 다른 개념이다. 하지만 코루틴과 스레드 모두 동시성 프로그래밍 문제를 해결할 수 있는 방법이며, 앞서 말한 대로 코루틴이 스레드보다 가볍기 때문에 이를 이해하는 것에 도움을 주기 위한 비유적 표현으로서 탄생했다고 생각한다.
정리하며 #
이번 글에서는 특정한 프로그래밍 언어의 구현체를 떠나 프로그래밍 개념으로서의 코루틴의 기본적인 개념에 관련해 작성해 보았다. 코루틴이 동시성 프로그래밍 방법의 한 종류라는 것과 대표적 동시성 프로그래밍 방법인 멀티스레딩과의 차이점도 비교해 보았다. 코루틴을 구현한 프로그래밍 언어별로 알아보는 것도 재미있을 것 같지만 물리적 한계상 모두 알아볼 수는 없을 것 같다. 대신 다음 코루틴 관련 글을 작성한다면 Kotlin에 집중한 내용으로 써 보려 한다.
동시성 프로그래밍(Concurrency Programming)이란 여러 작업이 동시에 진행되는 것 처럼 보이게 하는 방식을 말한다. 물리적으로 여러 작업이 동시에 동작하지 않더라도 짧은 시간 간격으로 번갈아 가며 처리하는 등의 기법을 사용해 마치 동시에 진행되는 것 같은 효과를 낸다. 실제로 기기의 한정된 자원 내에서 여러 작업을 처리해 작업이 막히지 않고 처리되도록 할 수 있다. ↩︎
협력적 멀티태스킹(Cooperative Multitasking)이란 멀티태스킹 방법 중 하나로, 각 작업(Task)이 자발적으로 동작 권한을 다른 작업에게 양보하는 방식으로 동작하는 멀티태스킹이다. 작업이 프로그래머가 원하는 시점에 다른 작업에게 권한을 양보하며 이를 통해서 특정 작업이 권한을 받지 못하고 오랫동안 멈추는 현상이 적고 권한이 강제로 전환되는 등에 따른 오버헤드가 적다. 반대 방법으로 선점형 멀티태스킹(Preemptive Multitasking)이 있다. ↩︎