goroutine은 Go 언어에서 제공하는 경량 스레드(lightweight thread) 개념으로, OS 수준 스레드와 달리 매우 적은 비용으로 생성·관리할 수 있는 동시성(concurrency) 실행 단위입니다. Go 런타임이 직접 스케줄링하여, 개발자는 go 키워드만 붙이면 별도의 고루틴에서 해당 함수를 실행하도록 만들 수 있습니다.
동시성(Concurrency)과 병렬성(Parallelism)에 대한 요구
Go 언어는 2009년 구글에서 개발을 시작했으며, 현대 프로그래밍 환경에서 점점 중요해지고 있는 동시성(Concurrency) 문제를 간단하고 효율적으로 해결하고자 설계되었습니다. 전통적인 스레드(Thread)를 이용한 동시성 프로그래밍 방식은 다음과 같은 문제가 있었습니다.
이러한 문제를 보다 단순하게 해결하기 위해 Go는 동시성 모델로 CSP(Communicating Sequential Processes) 모델을 채택하였고, 이를 더 쉽게 구현하기 위해 ‘작고 가벼운 스레드’인 고루틴을 도입하였습니다.
경량 스레드(Lightweight Thread)에 대한 요구
전통적인 OS 스레드는 생성 시나 스케줄링 시에 무거운 비용이 발생합니다. 반면에 고루틴은 Go 런타임 레벨에서 스케줄링되어 수많은(수천~수백만 개까지) 고루틴을도 운영체제 스레드에 비해 훨씬 적은 비용으로 관리할 수 있도록 설계되었습니다. 이러한 경량성은 Go 언어가 빠르고 효율적인 동시성 처리를 가능하게 만드는 핵심 요소입니다.
고성능 서버 및 네트워크 프로그램
Go는 네트워크 서버, 분산 시스템, 마이크로서비스 등에 활용될 때 뛰어난 성능과 간단한 코드 구현 방식을 제공합니다. 예를 들어 HTTP 서버를 구성할 때, 요청마다 고루틴을 생성하여 동시성을 극대화할 수 있습니다.
간단한 동시성 코드 구현
Go 언어는 “동시성은 복잡하지만, 코드는 단순해야 한다”라는 기조를 지향합니다. go 키워드를 사용하여 쉽게 고루틴을 만들고, 채널(channel) 이라는 Go만의 동시성 원리를 통해 고루틴 간 데이터를 안전하게 전달하고 동기화할 수 있습니다.
효율적인 리소스 활용
고루틴은 필요할 때만 생성되고, 실행이 일시 중단(sleep 등) 상태가 되면 다른 고루틴들에게 CPU 자원을 양보합니다. 또한 최소 2KB(Go 1.4 이전에는 스택 분할(Segmented stack)을 사용했으나, 현재는 동적으로 스택이 확장되는 방식을 사용)에 불과한 스택으로 시작해 필요할 때 점진적으로 스택을 키워나가므로 메모리를 효율적으로 관리할 수 있습니다.
func main() {
go func() {
fmt.Println("Hello from a goroutine!")
}()
// 메인 함수(고루틴)가 종료되면 모든 고루틴이 함께 종료되므로
// 잠시 대기하도록 Time.Sleep 또는 sync.WaitGroup 등을 사용할 수 있음
time.Sleep(1 * time.Second)
}
동기화 및 데이터 교환: 채널(Channels)
Go에서 고루틴 간 데이터 전달 및 동기화를 위해 가장 널리 사용되는 방법이 채널(channel) 입니다. 채널은 타입 안전(type-safe)한 FIFO 큐와 같은 역할을 하며, <- 연산자를 통해 데이터를 주고받습니다.
func main() {
ch := make(chan int)
go func() {
// 채널에 값 보내기
ch <- 42
}()
// 채널에서 값 받기 (동기화 발생)
val := <-ch
fmt.Println(val) // 42
}