select는 여러 채널에서의 데이터 수신 또는 종료를 기다릴 수 있는 Go의 강력한 구조입니다. 여러 케이스 중 하나가 준비되면 해당 블록이 실행되고, 나머지는 현재 반복에서 무시됩니다.
ch1 := make(chan int) ch2 := make(chan string) go func() { ch1 <- 42 }() go func() { ch2 <- "hello" }() select { case i := <-ch1: fmt.Println("ch1에서 수신됨:", i) case s := <-ch2: fmt.Println("ch2에서 수신됨:", s) default: fmt.Println("통신 없음") }
세부 사항:
select 내 채널이 어느 것도 읽기/쓰기 준비가 되어 있지 않고 default 블록이 없는 경우 어떻게 될까요?
답변: Select는 채널 중 하나라도 작업을 수행할 준비가 될 때까지 차단됩니다.
이야기
개발자는 채널을 종료하는 기능을 select를 통해 default 블록 없이 구현했으며, 함수가 "제어를 넘길 것"이라 믿었습니다. 채널은 비어 있었고, 함수는 select에서 영원히 차단되어 마이크로서비스의 데이터 흐름이 정지했습니다.
이야기
여러 채널과 함께 select를 사용하면서 일부 goroutine에서 채널이 닫힐 때 catch되지 않은 패닉이 발생하여 메모리 누수 및 오류가 발생했습니다. 채널 종료를 확인하는 로직(ok 변수를 통한 체크)이 구현되지 않았습니다.
이야기
한 프로젝트에서 개발자들은 차단을 방지하기 위해 select에 default 블록을 추가했지만, 이는 busy loop를 초래했습니다. select가 반복적으로 실행되어 default가 즉시 작동하고 CPU 사용률이 100%에 도달했습니다. 이는 채널로부터 이벤트를 기다리기보다는 적극적으로 CPU를 소모하게 만드는 결과였습니다.