Go routine(고루틴)
개요
Go routine(일반적으로 goroutine이라 표기)은 구글이 개발한 프로그래밍 언어 Go에서 제공하는 경량 스레드 형태의 동시성 실행 단위이다. 운영 체제 수준의 스레드에 비해 메모리 사용량이 매우 작고, 고도의 스케줄링 및 통신 메커니즘을 내장하고 있어 대규모 병렬 처리와 높은 동시성을 요구하는 어플리케이션에서 핵심적인 역할을 한다.
정의
Go routine은 Go 런타임에 의해 관리되는 독립적인 실행 흐름으로, go 키워드 뒤에 함수 호출을 붙이면 해당 함수가 새로운 Go routine으로 비동기 실행된다. 이때 생성되는 실행 흐름은 기존의 메인 실행 흐름과 동일한 주소 공간을 공유하지만, 자체적인 스택을 가지고 있어 서로 간에 메시지 전달이나 채널을 통한 안전한 데이터 교환이 가능하다.
역사와 배경
- 2009년 구글이 Go 언어를 발표하면서, 네트워크 서버와 분산 시스템에서 흔히 요구되는 대규모 동시성을 효율적으로 구현하기 위한 메커니즘으로 Go routine을 설계하였다.
- 기존의 POSIX 스레드(POSIX threads)와 달리, Go routine은 M:N 스케줄링 모델(다수의 Go routine을 소수의 운영체제 스레드(M)와 매핑) 을 채택해 높은 확장성을 확보하였다.
- 초기 버전부터 Go 런타임에 내장된 work stealing 기반 스케줄러가 도입되어, CPU 코어 수에 관계없이 수십만 개의 Go routine을 동시에 운용할 수 있다.
동작 원리
-
스택 관리:
- Go routine은 초기 크기가 몇 킬로바이트 수준인 동적 성장 스택을 사용한다. 실행 중 스택이 부족하면 자동으로 스택 복사가 일어나면서 스택 크기가 늘어난다.
- 이 메커니즘은 전통적인 OS 스레드가 고정된 수 메가바이트 스택을 할당받는 방식과 대비된다.
-
스케줄링:
- 런타임은 P(Processor) 라는 논리적 실행 단위와 M(Operating system thread) 를 사용해 Go routine(G)을 관리한다.
- P는 실행 가능한 Go routine 큐를 보유하고, M은 실제 OS 스레드와 매핑된다. P가 비어 있으면 다른 P에서 작업을 스틸링해 가져와 부하를 고르게 분산한다.
-
통신:
- Go routine 간의 데이터 교환은 채널이라는 동기화 프리미티브를 통해 이루어진다. 채널은 기본적으로 FIFO 큐이며, 송신/수신 연산은 자동으로 블로킹/언블로킹을 처리한다.
주요 특징
| 특징 | 설명 |
|---|---|
| 경량성 | 수십만 개까지 생성 가능, 메모리 오버헤드가 수 KB 수준 |
| 자동 스택 성장 | 동적 스택 관리로 메모리 효율성 향상 |
| M:N 스케줄링 | 수많은 Go routine을 제한된 OS 스레드에 매핑 |
| 채널 기반 통신 | 명시적 락 없이 안전한 데이터 교환 지원 |
| 가비지 컬렉션 연동 | 런타임이 스택 이동을 가비지 컬렉션과 조화시켜 메모리 누수 방지 |
| 선점형 실행 | 런타임 스케줄러가 일정 주기마다 Go routine을 선점해 공정성을 보장 |
사용 방법 (개념적 설명)
- 함수 호출:
go키워드 앞에 함수를 배치하면 해당 함수가 새로운 Go routine으로 시작된다. - 동기화: 필요에 따라 채널을 생성하고,
send와receive연산을 통해 데이터를 교환한다. - 종료 대기: 메인 Go routine이 종료되지 않도록 하기 위해
sync.WaitGroup등과 같은 동기화 도구를 활용한다(코드 예시는 제외). - 에러 처리: Go routine 내부에서 발생한 패닉은
recover를 이용해 복구하거나, 오류를 채널을 통해 전달한다.
장점 및 제한점
-
장점
- 높은 동시성 구현이 간결하고 직관적이다.
- 메모리 사용량이 작아 대규모 서비스에서 비용 효율적이다.
- 채널을 통한 통신으로 레이스 컨디션을 최소화한다.
-
제한점
- 순수 CPU 바운드 작업에서는 스케줄링 오버헤드가 성능에 영향을 줄 수 있다.
- Go routine 간에 공유 메모리를 직접 조작할 경우, 전통적인 락 메커니즘과 동일하게 레이스 조건이 발생할 수 있다.
- 현재 Go 런타임은 실시간 보장보다 높은 처리량을 목표로 설계돼, 실시간 시스템에는 적합하지 않을 수 있다.
다른 언어와의 비교
| 언어 | 동시성 모델 | 주요 차이점 |
|---|---|---|
| Java | 스레드 기반, ExecutorService 등 |
OS 스레드당 메모리 비용이 크고, 스레드 풀 관리가 필요 |
| C++ (std::thread) | OS 스레드 | 스택 크기와 스케줄링을 직접 관리해야 함 |
| Python (asyncio) | 이벤트 루프 기반 코루틴 | 단일 스레드·단일 코어에 제한되며, I/O 바인드에 최적 |
| Erlang | 프로세스 기반 (경량 프로세스) | 메모리 오버헤드가 Go routine보다 다소 크고, 메시지 전달 방식이 비슷하지만 언어 자체가 분산 시스템에 초점 |
| Rust (async/await) | Future 기반 비동기 | 런타임이 별도 필요하고, 스택 관리가 수동적이며, Go routine만큼 자동 스케줄링이 내장되지 않음 |
Go routine은 경량 스레드+채널이라는 조합으로, 위 언어들의 모델보다 간편하면서도 높은 확장성을 제공한다는 평가를 받고 있다.
구현 상세
-
런타임 구조
- M(machine) : 실제 OS 스레드
- P(processor) : 실행 컨텍스트, 스케줄링 큐 보유
- G(goroutine) : 실행 중인 작업 단위
- 스케줄러는 P당 하나의 M을 유지하며, P가 작업을 모두 소진하면 다른 P에서 작업을 탈취한다(steal).
-
스택 복사 메커니즘
- Go routine이 차지하는 스택은 segment tree 형태로 관리되며, 스택이 성장할 때 현재 스택을 새로운 메모리 영역에 복사하고 포인터를 갱신한다. 이 과정은 GC와 연계돼 안전성을 확보한다.
-
GC와의 연동
- Go 런타임의 가비지 컬렉터는 동시 마크-스위핑 방식을 사용하고, Go routine이 스택을 이동할 경우 마크 단계에서 적절히 추적한다.
관련 기술
- 채널(Channel) : Go routine 간 통신을 위한 동기화 프리미티브
- sync 패키지 :
WaitGroup,Mutex,Cond등 동시성 제어 도구 제공 - context 패키지 : Go routine 간 작업 취소와 타임아웃 전파 메커니즘
- runtime/trace : Go routine의 동작을 시각화하고 분석하는 트레이싱 도구
참고 문헌
- Go 언어 공식 스펙, “Goroutine” 섹션.
- “The Go Programming Language” – Alan A. A. Donovan, Brian W. Kernighan.
- “Concurrency in Go” – Katherine Cox‑Buday.
- Go 런타임 설계 문서, “Scheduler” 및 “Stack Management” 파트.
(위 내용은 2026년 현재까지 공개된 자료와 Go 언어 공식 문서를 근거로 작성되었습니다.)