🧵 Go의 "경량 스레드"란?
Go에서는 스레드(thread) 대신에 **고루틴(goroutine)**이라는 걸 써.
이 고루틴은 OS 스레드보다 훨씬 가볍고, 수천수백만 개도 동시에 쓸 수 있어!
👉 한마디로?
고루틴은 Go가 만든 엄청 가벼운 "경량 스레드"
📌 고루틴(goroutine)의 특징
특징 | 설명 |
✅ 가볍다 | 시작 시 스택 크기가 약 2KB (필요 시 자동 증가) |
✅ 수십만 개 생성 가능 | OS 스레드에 비해 매우 적은 자원 사용 |
✅ OS 스레드와 매핑 아님 | Go 런타임이 직접 스케줄링함 |
✅ 빠르게 생성/종료 | 고루틴은 함수를 go 키워드로 호출하면 바로 생성됨 |
📦 예제 코드
package main
import (
"fmt"
"time"
)
func sayHello() {
fmt.Println("Hello from goroutine!")
}
func main() {
go sayHello() // 고루틴 시작 (백그라운드 실행)
time.Sleep(1 * time.Second) // 메인 고루틴이 기다려줘야 결과가 보임
}
✅ 출력:
Hello from goroutine!
go 함수이름() 하면 고루틴이 만들어져 비동기적으로 실행돼!
⚙️ 고루틴은 어떻게 돌아가?
Go에는 자체적으로 만든 스케줄러가 있어:
🔁 M:N 스케줄링
- M개의 고루틴(G)
- N개의 OS 스레드(M)
- 그 사이를 **P(Processor)**가 관리함
🌐 스케줄러 핵심 구성요소
요소 | 설명 |
G (goroutine) | 실행될 코드와 스택 정보를 가진 단위 (실행할 함수, 상태 등) |
P (process) | 실행 context, 스케줄링을 담당하며 G큐를 가짐 |
M (machine) | 실제 OS 스레드, G를 실행함 |
📌 Go 런타임이 이 세 가지를 조합해서 효율적으로 실행함:
G (goroutine) → P (processor) → M (OS thread)
이 덕분에:
- CPU 코어 수에 맞춰 고루틴들을 자동 분배
- I/O 작업 중 다른 고루틴으로 빠르게 전환
- 멀티코어 활용도 잘됨
🤔 고루틴 vs 스레드
항목 | 고루틴(goroutine) | OS 스레드(thread) |
생성 비용 | 매우 작음 (2KB) | 큼 (수십 KB ~ MB) |
수량 | 수십만 개 가능 | 수천 개 한계 |
관리 주체 | Go 런타임 (유저 레벨) | OS 커널 |
스케줄링 | M:N, Go가 직접 함 | 1:1, OS가 스케줄링 |
I/O 블로킹 | 자동 전환 처리 | 다른 스레드도 블로킹됨 |
안전성 | 내장된 동기화 도구 (채널 등) | 직접 락 관리 필요 |
📡 동시성(Concurrency)과 병렬성(Parallelism)
- 고루틴 덕분에 Go는 동시성 프로그래밍이 매우 쉬움
- runtime.GOMAXPROCS(N)을 설정하면 병렬 처리도 가능
🔒 고루틴 간 통신: 채널(channel)
고루틴 간 데이터를 주고받을 땐 채널을 씀:
ch := make(chan string)
go func() {
ch <- "Hello from goroutine"
}()
msg := <-ch
fmt.Println(msg)
📌 채널을 통해 안전하게 데이터 교환 가능, 락 없이도 동기화가 됨!
✅ 요약
- Go의 고루틴은 OS 스레드보다 훨씬 가볍고 효율적
- 내부적으로 Go 런타임이 M:N 스케줄링을 수행
- 고루틴끼리는 채널로 통신
- 동시성(concurrency)을 간단하고 안정적하게 구현할 수 있게 해줌
반응형