Go의 select 문은 통신 작업 간의 공정성을 보장하고 기아를 방지하기 위해 균등한 의사-무작위 선택을 사용합니다. select 문의 여러 케이스가 동시에 준비되어 있는 경우, 런타임은 케이스 순서의 무작위 순열을 생성하고 이를 순차적으로 평가하여 하나가 성공할 때까지 진행합니다. 이 설계는 하나의 Channel이 지속적으로 실행을 지배하지 않도록 보장하여, 모든 준비된 케이스에 대해 선택 확률을 균등하게 분배합니다.
세 개의 독립적인 거래소 피드에서 시장 데이터를 집계하는 주요 Goroutine이 있는 고주파 거래 플랫폼을 생각해 보십시오. 이 피드는 각기 다른 Channels인 NYSE, NASDAQ, Forex를 통해 업데이트를 제공합니다. Forex 채널은 마이크로초 단위의 통화 변동을 전송하는 반면, NYSE는 10밀리초마다 업데이트하고, NASDAQ은 정상 조건에서 50밀리초마다 대량 거래 알림을 전송합니다.
만약 Go가 select 케이스를 고정된 어휘 순서로 평가한다면, Forex 채널의 지속적인 준비 상태는 변동성이 큰 거래 기간 동안 NASDAQ 알림을 재앙적으로 기아에 빠뜨릴 것입니다. 이러한 기아는 집계 엔진이 중요한 거래 실행을 놓치게 하여 결정적인 최상의 실행 보고 요구 사항을 위반할 수 있습니다. 시스템은 상대 속도나 도착 빈도에 관계없이 모든 데이터 소스가 처리 시간을 받을 수 있도록 보장하는 공정성 메커니즘이 필요했습니다.
우리는 처음에 애플리케이션 코드에서 채널을 순환하는 회전 인덱스를 유지하여 수동 라운드로빈을 구현하는 것을 고려했습니다. 이 접근 방식은 마지막으로 서비스된 채널을 명시적으로 추적하고 커서를 적절히 이동하여 결정론적인 공정성을 제공할 것입니다. 그러나 이 솔루션은 상당한 복잡성을 야기하여, 동시 접근에서 공유 상태를 관리해야 하고, 여러 Channels에서 대기하는 의도를 깔끔한 구문으로 흐리게 만들었습니다.
두 번째 접근 방식은 높은 빈도의 Forex 업데이트를 인위적으로 조절하여 느린 채널에 대역폭을 만들기 위한 가중 우선 순위 시스템을 구현하는 것이었습니다. 이는 메시지 처리량에 대한 세밀한 제어를 가능하게 했지만, 시장 변동성 조건에 따라 조정 속도를 지속적으로 조정해야 했습니다. 유지 관리 부담이 과도하여, 잘못 구성된 경우에는 시스템이 원활한 처리보다 공정한 분포가 필요한 플래시 크래쉬 동안 중요한 가격 변동을 조용히 놓치는 문제가 발생할 수 있었습니다.
결국 우리는 애플리케이션 레이어 복잡성 없이 통계적 공정성을 제공하는 Go의 내장된 의사-무작위 select 동작에 의존했습니다. 균등 분포는 수백만 번의 반복에서 각 Channel이 실제 준비 빈도에 비례하는 실행 기회를 받을 수 있도록 보장하여, 기아 사건을 전부 제거하였습니다. 비결정론적 특성은 스트레스 테스트 중에 결정론적 순서가 숨겼던 잠재적인 레이스 조건을 드러내는 데 놀랍게도 도움이 되었습니다.
왜 Go는 select 케이스에 대해 특정한 평가 순서를 보장하지 않는가?
Go는 준비된 Channels 간의 선택이 비결정론적임을 명시하여 개발자가 구현 특정 순서에 의존하는 코드를 작성하지 않도록 합니다. 런타임은 버전 간에 무작위화 알고리즘을 변경할 수 있으므로, 프로그램은 소스 위치와 관계없이 모든 케이스를 동등하게 가능성으로 취급해야 합니다. 이러한 설계 철학은 Goroutines가 컴파일러 업그레이드 중 깨질 수 있는 타이밍 가정이나 채널 우선 순위에 우연히 의존하지 않도록 강력한 동시성 패턴을 강요합니다.
언어 원시 자료형을 사용하여 select가 하나의 Channel을 다른 Channel보다 우선시하도록 강제할 수 있는가?
Go의 select는 본질적으로 공정하지만, 개발자는 중첩된 select 문이나 보조 제어 Channels를 사용하여 우선 순위를 시뮬레이션할 수 있습니다. 하지만 이는 관용적인 Go 스타일을 위반하게 됩니다. 하나의 안티 패턴은 빠른 Channels를 타임아웃 로직으로 감싸거나 바쁜 루프에서 기본 케이스를 사용하는 방법이며, 이는 바쁜 대기를 발생시켜 CPU 사이클을 낭비하게 됩니다. 올바른 접근 방식은 언어 기능으로서의 균등한 무작위성을 받아들이고, 동등하게 대기 중인 Channels 간에 엄격한 우선 순위를 요구하지 않도록 아키텍처를 재설계하는 것입니다.
어떤 동기화 메커니즘이 select가 여러 Channels에서 원자적으로 대기하도록 허용하는가?
select는 모든 관련 Channels의 대기 큐에 Goroutine을 동시에 등록한 후 잠자게 하여 대기 상태의 일관된 스냅샷을 생성합니다. 어떤 Channel이 준비되면, Goroutine을 깨우고, 이는 이후 작업을 진행하기 위해 잠금을 확보하기 위해 경쟁해야 합니다. 이 원자적 다중 등록은 깨어 남을 놓치는 것을 방지하고 여러 Channels에서 데이터가 동시에 수신될 때도 정확히 하나의 케이스가 실행되도록 보장합니다. 하지만 후보자들은 종종 select가 폴링하거나 중앙 브로커를 사용하는 것으로 오해합니다.