프로그래밍백엔드 개발자

Go에서 실행 흐름 관리와 스케줄러는 어떻게 구현되었나요? 병렬 작업을 설계할 때 고려해야 할 특성, 내부 구성 요소 및 제한 사항은 무엇인가요?

Hintsage AI 어시스턴트로 면접 통과

답변.

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) }

주요 특징:

  • 고루틴은 운영 체제 스레드보다 저렴하며, 빠르고 간단하게 생성되고 추가 관리가 필요하지 않습니다.
  • Go의 스케줄러는 사전 중단(preemptive)을 통해 런타임 내의 특수 지점을 사용하여 고루틴의 올바른 전환을 보장합니다.
  • GOMAXPROCS는 사용하는 운영 체제의 스레드 수를 설정하지만, 일반적으로 수동으로 설정할 필요는 없습니다.

농담 질문.

각 고루틴이 별도의 CPU 코어에서 동시에 실행될까요?

아니요. 고루틴은 멀티플렉스되며 스케줄러가 실제로 동시에 실행되는 작업 수를 결정합니다.

특정 고루틴/스레드의 실행을 수동으로 관리할 수 있나요?

아니요. Go 런타임은 직접적인 스케줄링 인터페이스를 제공하지 않습니다. 예외적으로 GOMAXPROCS는 운영 체제 스레드 수를 설정하는 데 사용됩니다.

많은 고루틴이 자동으로 프로그램 속도를 증가시키나요?

아니요. 많은 수의 경쟁 작업은 추가 오버헤드를 초래할 수 있습니다: 컨텍스트 전환, 자원에 대한 경쟁, GC 시간 증가 및 메모리 소비 증가.

일반적인 오류와 안티패턴

  • 제한 없이 수백만 개의 고루틴을 생성하는 것 (고루틴 유출).
  • sync.WaitGroup/채널 대신 time.Sleep을 통한 완료 대기.
  • 아키텍처에 대한 이해 없이 GOMAXPROCS 수를 지나치게 추구하는 것.

실제 사례

부정적인 사례

마이크로서비스가 요청을 병렬로 처리하며 고루틴 수를 제한하지 않고 WaitGroup으로 완료를 기다리지 않았습니다. 결과: 증가된 응답 시간, 데이터 레이스, 예측할 수 없는 타임아웃.

장점:

  • 병렬성 추가가 용이함;

단점:

  • 메모리 제한, 누수, 복잡한 진단.

긍정적인 사례

작업자 관리자가 고루틴 풀을 통해 구현되어 동시에 실행되는 작업 수를 세마포어 또는 채널을 통해 제한했습니다. WaitGroup이 모든 작업의 완료를 올바르게 기다립니다.

장점:

  • 관리되는 병렬성, 테스트 반복 가능성, 성공적인 확장.

단점:

  • 제한 및 동기화를 위한 추가 코드가 필요함; 교착 상태에 대한 테스트가 필요함.