Go는 본래 고성능 네트워크 및 병렬 프로그램 작성을 위해 설계되었기 때문에 내장 스케줄러와 고루틴을 통한 간단한 병렬성 관리에 특별한 관심을 기울였습니다. 대부분의 언어와 달리, 운영 체제 스레드를 사용자 언어가 직접 관리하는 대신, Go에서는 고루틴이 훨씬 가볍고 수천 개가 고정된 수의 시스템 스레드 위에서 동시에 실행될 수 있습니다.
문제: 스레드를 직접 관리하는 것은 어렵습니다: 이로 인해 자원 낭비, 데이터 레이스 및 메모리 관리의 복잡성이 발생합니다.
해결책: Go는 M:N 모델을 사용하여 다수의 고루틴(M)이 제한된 수의 운영 체제 스레드(N)에 멀티플렉스됩니다. 이는 Go 런타임 수준에서 구현된 스케줄러가 자동으로 고루틴의 실행을 균형 있게 조정하고 재분배하여 책임지도록 합니다. 프로그래머는 운영 체제 스레드가 아닌 고루틴의 실행과 동기화만 관리합니다.
코드 예시:
package main import ( "fmt" "time" ) func worker(id int) { fmt.Printf("Worker %d starting ", id) time.Sleep(time.Second) fmt.Printf("Worker %d done ", id) } func main() { for i := 0; i < 5; i++ { go worker(i) } time.Sleep(2 * time.Second) }
주요 특징:
각 고루틴이 별도의 CPU 코어에서 동시에 실행될까요?
아니요. 고루틴은 멀티플렉스되며 스케줄러가 실제로 동시에 실행되는 작업 수를 결정합니다.
특정 고루틴/스레드의 실행을 수동으로 관리할 수 있나요?
아니요. Go 런타임은 직접적인 스케줄링 인터페이스를 제공하지 않습니다. 예외적으로 GOMAXPROCS는 운영 체제 스레드 수를 설정하는 데 사용됩니다.
많은 고루틴이 자동으로 프로그램 속도를 증가시키나요?
아니요. 많은 수의 경쟁 작업은 추가 오버헤드를 초래할 수 있습니다: 컨텍스트 전환, 자원에 대한 경쟁, GC 시간 증가 및 메모리 소비 증가.
마이크로서비스가 요청을 병렬로 처리하며 고루틴 수를 제한하지 않고 WaitGroup으로 완료를 기다리지 않았습니다. 결과: 증가된 응답 시간, 데이터 레이스, 예측할 수 없는 타임아웃.
장점:
단점:
작업자 관리자가 고루틴 풀을 통해 구현되어 동시에 실행되는 작업 수를 세마포어 또는 채널을 통해 제한했습니다. WaitGroup이 모든 작업의 완료를 올바르게 기다립니다.
장점:
단점: