Go Concurrency & Runtime
Go Concurrency 및 런타임의 정의, 범위, 선행 지식, 학습 주제, 참고 근거를 정리한 CS&E 학습 노드입니다.
Article
M
Me
hyunyoun's Blog
programming-languages-compilersprogramming-languagescompilerslanguage-platformsecosystemsgo-concurrencyruntimelanguages-compilers9 min read
1. Overview
Go의 동시성과 런타임(Go Concurrency & Runtime)은 "메모리를 공유하여 통신하지 말고, 통신하여 메모리를 공유하라"는 철학으로 클라우드 네이티브와 마이크로서비스 환경을 겨냥해 설계된 Go 언어의 동시성 런타임을 다룹니다.
학습자는 OS 스레드보다 100배 가볍게 수백만 개를 띄울 수 있는 **고루틴(Goroutine)**의 Mcontext.Context)를 이용한 타임아웃 전파, 뮤텍스(sync.Mutex)와 채널의 선택 기준, 그리고 Go 1.18 이후 도입된 제네릭(Generics) 기반의 타입 안전성과 런타임 가비지 컬렉터 설계 철학까지 익혀 대규모 분산 서버 코어 역량을 확보합니다.
2. Scope & Boundaries
In-Scope
- CSP 동시성 모델 (CSP Concurrency): 고루틴(
go), 채널(chan), Select 구문(select), 데드락 탐지. - 고루틴 스케줄러 아키텍처 (Scheduler): M
스케줄링, G(Goroutine), M(Machine/OS Thread), P(Processor/Local Queue), Work Stealing(작업 가져오기). - 채널 내부 메커니즘 (Channel Internals): 락킹 메커니즘, 링 버퍼(버퍼드 채널), 휴면 대기(Hchan 구조체), 블로킹/논블로킹.
- 클라우드 네이티브 인프라 (Cloud Native & Stdlib):
context.Context(타임아웃, 취소),sync패키지(Mutex, WaitGroup), 컴파일 속도와 정적 링킹 바이너리.
Out-of-Scope
- 웹 프레임워크 생태계: Gin, Fiber 등의 프레임워크 사용법 → 백엔드 인프라/프레임워크 영역.
- 저수준 어셈블리 및 cgo 최적화: C 코드 링킹에 따른 스케줄러 성능 하락 상세 분석 → 심화 최적화 영역.
Boundaries
- CSP(채널) vs 공유 메모리(뮤텍스) 트레이드오프: Go 커뮤니티의 금언은 "채널을 선호하라"이지만, 모든 상황에 채널이 정답은 아닙니다. 데이터를 여러 파이프라인 스테이지로 전달(데이터 흐름)할 때는 채널이 직관적이고 안전하지만, 단순히 여러 고루틴이 하나의 캐시 딕셔너리에 접근(상태 락킹)할 때는
sync.Mutex가 오버헤드가 적고 10배 이상 빠릅니다. 문제의 본질(흐름 전달인가, 상태 보호인가)에 따라 도구를 취사선택해야 합니다.
3. Counterexample
- 고루틴 누수 (Goroutine Leak): 채널에 데이터를 보내는 고루틴 수십 개를 띄웠는데, 수신자 고루틴이 에러로 종료됨. 송신 고루틴들은 채널 버퍼가 꽉 찬 채로
ch <- data라인에서 영원히 블로킹(무한 대기). OS 스레드는 블로킹 해제되지만, 2KB짜리 고루틴 메모리는 영영 GC되지 않는 누수가 발생합니다. 클라우드 서버에서 며칠 뒤 메모리 고갈로 서버가 크래시.context패키지나close(ch)를 통해 모든 고루틴에게 확실한 취소(Cancellation) 시그널을 보내는 종료 설계가 필수입니다. - 채널 오용으로 인한 글로벌 뮤텍스보다 느린 성능 (Channel Overhead Overkill): 단순한
Counter구조체의Increment()메서드를 스레드 안전하게 만들겠다며 채널 기반 큐를 구현. 고루틴마다 채널 락킹, 링 버퍼 할당, 컨텍스트 스위칭 등 채널 내부 오버헤드가 발생하여 원시sync.atomic.AddInt64()나sync.Mutex를 사용한 코드보다 100배 느려질 수 있습니다. 채널은 모든 상황의 해법이 아닙니다.
4. Prerequisites
- 동시성 기본 (Basic): 데드락, 레이스 컨디션, 뮤텍스의 기본 개념. (03-02-02 Thread Synchronization & Deadlocks)
- 프로세스 통신(IPC) 기초 (Recommended): 프로세스 간 통신 파이프라인 개념은 채널을 이해하는 좋은 유추입니다. (03-02-01 Processes & Threads)
5. Learning Map
6. Learning Topics
Basic
Core Topic 01: 공유보다 통신, 고루틴과 CSP 모델 (Goroutine & CSP)
- Why to Learn: 복잡한 Mutex Lock/Unlock 흐름을 줄이고, 데이터가 스레드 경계를 파이프처럼 이동하는 Go의 동시성 모델을 체득하기 위함입니다.
- What to Learn:
- Concepts: 고루틴(
go키워드), 스택 메모리(2KB로 시작, 동적 확장), 채널(chan T), 언버퍼드 채널(Unbuffered Channel, 동기화 강제), CSP(Communicating Sequential Processes). - Skills:
go func()비동기 실행, 채널로 데이터 생산/소비 파이프라인 구축.
- Concepts: 고루틴(
- How to Learn:
- 1단계: 고루틴 생성:
go doHomework(). 함수 앞에go만 붙이면 OS 스레드 생성 비용(MB 단위 스택) 없이 독립적인 실행 흐름이 생성됨을 살펴봅니다. 수십만 개까지 운영할 수 있는 이유를 런타임 관점에서 확인합니다. - 2단계: 채널 통신:
ch <- data(송신),<-ch(수신). 언버퍼드 채널은 송신자와 수신자가 동시에 준비되지 않으면 먼저 온 쪽이 기다립니다(블로킹). 락(Lock) 없이 데이터 전달 자체가 동기화 포인트가 되는 CSP 모델 동작을 살펴봅니다.
- 1단계: 고루틴 생성:
- Implement: Go 생산자-소비자 패턴 모사. 파이썬
queue.Queue기반go()시뮬레이터 작성. 생성자 스레드는 1~10 데이터 큐에 삽입, 소비자 스레드는 큐에서 빼내 곱하기 출력. 상태 공유 리스트를 뮤텍스로 잠그는 기존 코드 대비 큐 기반 파이프라인의 간결성 증명.
Recommended
Core Topic 02: 수백만 고루틴과 G-M-P 스케줄러 (M Scheduler)
- Why to Learn: "어떻게 Go는 OS 스레드가 8개뿐인데 고루틴 100만 개를 블로킹 없이 돌릴 수 있는가?" 그 이면에 있는 Work Stealing 알고리즘과 런타임 스케줄러를 이해하여 고성능 트래픽 서버 설계 역량을 갖추기 위함입니다.
- What to Learn:
- Concepts: M
스케줄링(M개 OS 스레드가 N개 고루틴 실행), G(Goroutine), M(Machine, OS 스레드), P(Processor, 로컬 실행 큐), 작업 가져오기(Work Stealing). - Skills: 시스템 콜 블로킹(네트워크 I/O) 시 M 탈착 방지 전략 이해.
- Concepts: M
- How to Learn:
- 1단계: G-M-P 모델: P는 CPU 코어 수(GOMAXPROCS)만큼 생성. 각 P는 고루틴(G)이 담긴 로컬 큐를 가지며 1개의 OS 스레드(M)에 붙어 G를 하나씩 꺼내 실행합니다. 컨텍스트 스위칭(레지스터 교체)이 유저 스페이스에서 일어나 OS 오버헤드를 줄이는 메커니즘을 살펴봅니다.
- 2단계: 작업 가져오기(Work Stealing): P1의 로컬 큐가 비면, 쉬고 있는 P1의 M은 P2의 로컬 큐에서 고루틴 일부를 가져와(Steal) 실행하여 멀티코어 부하를 맞추는 동작을 살펴봅니다.
- Implement: 파이썬 G-M-P 스케줄러 시뮬레이터. P1(큐: G1, G2, G3), P2(큐: 비어있음). 시뮬레이션 루프가 P2가 비었음을 감지하고 P1 큐에서 G2, G3을 스틸하여 P2 큐로 옮기는 로드 밸런싱 로그 출력 데모.
Practical
Core Topic 03: 타임아웃과 멀티플렉싱, 채널 내부와 Select 구문 (Channel Internals & Select)
- Why to Learn: 런타임 내부의
HchanC구조체 메커니즘을 이해하여 버퍼드/언버퍼드 채널을 적재적소에 쓰고, Go 네트워크 서버의 핵심인select멀티플렉싱(이벤트 루프) 기술을 익히기 위함입니다. - What to Learn:
- Concepts:
Hchan구조체(버퍼 배열, 뮤텍스, 송수신자 대기 큐), 버퍼드 채널(Buffered Channel, 비동기 큐),select구문, 채널 닫기(close),for range수신. - Skills: 타임아웃 구현, 다중 채널 동시 대기 처리 로직.
- Concepts:
- How to Learn:
- 1단계: 버퍼드 채널 내부: 버퍼가 10인 채널 생성 시 런타임은 원형 버퍼(Ring Buffer)와 뮤텍스를 할당. 10개까지는 송신 고루틴이 락 획득 후 버퍼 복사 후 즉시 블로킹 해제. 꽉 차면 송신 고루틴 자체를 수면(Park) 상태로 대기 큐에 넣는 런타임 동작을 살펴봅니다.
- 2단계: Select 멀티플렉싱:
select { case msg := <-ch1: ... case <-time.After(1*Second): ... }. 채널 여럿 중 준비된 것을 무작위(Random fairness)로 처리. 네트워크 요청 후 1초 초과 시 타임아웃 채널이 먼저 활성화되어 폴백 처리하는 핵심 디자인 패턴을 살펴봅니다.
- Implement: Go
select폴링 구조 파이썬 모사. 두 개의queue.Queue대기. 루프를 돌며queue.Empty예외 검사로 Non-blocking 수신 시도. Q1에 값 들어오면 로직 실행, 1초 지나면 Timeout 예외 터뜨려 탈출하는 이벤트 멀티플렉서 장난감 제작.
Advanced
Core Topic 04: 취소 전파와 분산 시스템, Context 와 Cloud Native (Context & Stdlib)
- Why to Learn: 마이크로서비스 아키텍처(MSA)에서 A 서버 → B 서버 → C DB로 이어지는 분산 요청의 생명 주기(타임아웃, 사용자 취소)를 전파하기 위해 Go가 표준으로 채택한
context.Context설계 철학을 이해하기 위해서입니다. - What to Learn:
- Concepts:
context.Context(요청 스코프 데이터 및 취소 시그널 트리), 취소 전파(Cancellation Propagation), 단일 정적 바이너리(Static Binary Compilation), Go 가비지 컬렉터 설계 철학(Throughput 희생 < Low Latency 보장). - Skills:
context.WithTimeout,context.WithCancel, 백그라운드 고루틴 생명 주기 동기화.
- Concepts:
- How to Learn:
- 1단계: 컨텍스트 트리: 상위 핸들러에서
ctx, cancel = context.WithTimeout(..., 5s). 하위 DB 조회 함수, 외부 API 호출 함수에 전부 이ctx인자 전달. 사용자가 브라우저 취소 버튼을 누르거나 5초 초과 시, 트리 하위 모든 고루틴에게<-ctx.Done()시그널이 전파되어 연쇄 중단되는 동작을 살펴봅니다. 고루틴 누수를 줄이는 기본 설계입니다. - 2단계: Cloud Native 지향점: Go는 모든 의존성 라이브러리를 단 하나의 15MB 실행 파일(Statically Linked Binary)로 컴파일합니다. 도커(Docker) 컨테이너 안에 OS 라이브러리(
libc.so)조차 필요 없이 바이너리 하나만 넣으면 실행되는 배포 편의성의 의미를 살펴봅니다.
- 1단계: 컨텍스트 트리: 상위 핸들러에서
- Implement: 파이썬
Context모방 클래스 구현. 부모 Context 생성 후 자식 Context 2개 파생. 부모에서cancel()메서드 호출 시, 등록된 모든 자식 Context의 콜백 리스트가 연쇄적으로 순회하며is_cancelled = True설정. Worker 스레드들이 이 변수를 감시하다 즉각 종료되는 트리 구조 취소 전파 시연.
7. Terminology
8. References
Primary
- [P1] CS2023 - Parallel and Distributed Computing (PDC) - Concurrency Mechanisms
- [P5] SFIA - Software Development (PROG) - Network Programming
Secondary
- [Concurrency in Go] Katherine Cox-Buday - Goroutines, Channels, and Patterns
- [Go in Action] William Kennedy - Go Runtime and Scheduler Internals
Industry
- [Go Dev Blog] - Share Memory By Communicating
- [Go Runtime Source Code] - The M
Scheduler and Work Stealing Algorithms
9. Final Checklist
Primary
- OS 스레드(Thread)를 10만 개 띄울 때 발생하는 컨텍스트 스위칭(Context Switching) 증가와 OOM(Out of Memory)을, 고루틴(Goroutine)이 어떻게 완화하는지 설명할 수 있는가?
- "메모리를 공유하여 통신하지 말고, 통신하여 메모리를 공유하라(Share memory by communicating)"는 Go 언어의 철학이 뮤텍스(Mutex) 데드락을 어떻게 회피하는지 논증할 수 있는가?
Secondary
- 채널(Channel)에 버퍼가 없을 때(Unbuffered), 데이터를 보내는 고루틴과 받는 고루틴이 랑데부(Rendezvous) 동기화되어 강제로 실행이 블로킹(Blocking)되는 원리를 증명할 수 있는가?
- Go의 M
스케줄러가 특정 고루틴이 파일 I/O나 네트워크 대기로 블로킹되었을 때, 어떻게 해당 스레드를 멈추지 않고 다른 고루틴을 실행(Multiplexing)하는지 설명할 수 있는가?
Industry
- 다중 코어 시스템에서 빈 스레드가 바쁜 스레드의 로컬 큐에서 작업을 가져오는 'Work Stealing' 알고리즘이 마이크로서비스의 TPS(초당 트랜잭션)를 높이는 아키텍처를 설계할 수 있는가?
-
select구문을 활용하여 여러 채널의 이벤트를 논블로킹(Non-blocking)으로 동시에 기다리며, 타임아웃(Timeout)과 고루틴 누수(Goroutine Leak) 방지용 취소 토큰(Context) 파이프라인을 구축할 수 있는가?