Select 문
Coddy GO 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 107개 중 62번째.
select 문은 고루틴이 여러 채널 연산을 동시에 대기할 수 있게 해줍니다. switch 문과 비슷하지만, 각 case는 채널의 전송 또는 수신과 관련이 있습니다. 여러 채널이 준비되면 select는 그중 하나를 무작위로 선택합니다.
func main() {
ch1 := make(chan string)
ch2 := make(chan string)
go func() { ch1 <- "from channel 1" }()
go func() { ch2 <- "from channel 2" }()
select {
case msg1 := <-ch1:
fmt.Println(msg1)
case msg2 := <-ch2:
fmt.Println(msg2)
}
}select 없이, ch1으로부터 수신하는 것은 데이터가 도착할 때까지 차단되어 잠재적으로 ch2의 데이터를 놓칠 수 있습니다. select를 사용하면, 어느 채널이든 먼저 데이터를 수신하는 쪽이 처리됩니다.
default 케이스는 select를 비차단(non-blocking) 방식으로 만듭니다. 준비된 채널이 없으면 즉시 실행됩니다:
select {
case msg := <-messages:
fmt.Println("Received:", msg)
default:
fmt.Println("No message available")
}일반적인 패턴은 select를 time.After와 결합하여 타임아웃을 구현하는 것입니다. time.After는 지정된 시간이 지난 후 값을 수신하는 채널을 반환합니다:
select {
case result := <-ch:
fmt.Println("Got result:", result)
case <-time.After(2 * time.Second):
fmt.Println("Timeout!")
}이것은 채널 작업이 결코 완료되지 않을 수 있는 상황에서 프로그램이 무한정 대기하는 것을 방지합니다.
챌린지
쉬움여러 개의 동시 데이터 소스를 처리하기 위해 select 문을 사용하는 서비스 모니터를 만들어 보겠습니다. 여러분의 모니터는 서로 다른 서비스 채널을 동시에 수신하고, 데이터가 도착하거나 업데이트가 없을 때 적절하게 응답할 것입니다.
코드는 두 개의 파일로 구성됩니다:
monitor.go: select 기반의 채널 처리를 사용하여 서비스 모니터링 로직을 정의합니다.Name(string)과Status(string) 필드를 가진ServiceUpdate구조체를 생성합니다.primary chan ServiceUpdate,backup chan ServiceUpdate,done chan bool의 세 가지 채널을 인자로 받는Monitor함수를 구현합니다. 이 함수는select를 사용하여 세 채널을 동시에 수신해야 합니다:primary로부터 수신할 때:Primary: [Name] is [Status]형식의 문자열을 반환합니다.backup으로부터 수신할 때:Backup: [Name] is [Status]형식의 문자열을 반환합니다.done으로부터 수신할 때:Monitoring stopped를 반환합니다.
단일
updates chan ServiceUpdate를 인자로 받고default케이스가 있는select를 사용하여 비차단(non-blocking) 검사를 수행하는CheckStatus함수를 구현합니다:- 업데이트가 가능한 경우:
Update available: [Name]을 반환합니다. - 업데이트가 없는 경우 (default 케이스):
No updates pending을 반환합니다.
main.go: 채널을 설정하고 차단(blocking) 및 비차단(non-blocking) select 동작을 모두 보여줍니다.실행할 시나리오를 결정하는 모드(
primary,backup,stop, 또는check)를 입력으로부터 읽습니다.primary,backup,stop모드의 경우: 서비스 이름과 상태도 읽습니다. 세 개의 채널을 생성하고, 모드에 따라 적절한 채널에 데이터를 전송하는 고루틴을 실행한 다음,Monitor를 호출하고 그 결과를 출력합니다.check모드의 경우: 서비스 이름과 상태, 그리고 확인 전에 데이터를 보낼지 여부를 나타내는 플래그(send또는nosend)를 읽습니다. 용량이 1인 버퍼드 채널을 생성합니다. 플래그가send인 경우,CheckStatus를 호출하기 전에 채널에 업데이트를 보냅니다.CheckStatus의 결과를 출력합니다.
다음과 같은 입력이 제공됩니다:
- 1행: 모드 (
primary,backup,stop, 또는check) - 2행: 서비스 이름
- 3행: 서비스 상태
- 4행 (
check모드인 경우에만):send또는nosend
예를 들어, 다음과 같이 입력되면:
primary
Database
healthy출력은 다음과 같아야 합니다:
Primary: Database is healthy그리고 다음과 같이 입력되면:
check
Cache
active
nosend출력은 다음과 같아야 합니다:
No updates pending치트 시트
select 문은 고루틴이 여러 채널 연산을 동시에 기다릴 수 있게 해줍니다. switch 문과 비슷하게 작동하지만, 각 케이스는 채널의 송신 또는 수신과 관련이 있습니다:
select {
case msg1 := <-ch1:
fmt.Println(msg1)
case msg2 := <-ch2:
fmt.Println(msg2)
}여러 채널이 준비되었을 때, select는 무작위로 하나를 선택합니다. select가 없다면, 하나의 채널에서 수신하는 동안 블로킹되어 다른 채널의 데이터를 놓칠 수 있습니다.
default 케이스는 select를 비차단(non-blocking) 방식으로 만듭니다. 준비된 채널이 없으면 즉시 실행됩니다:
select {
case msg := <-messages:
fmt.Println("Received:", msg)
default:
fmt.Println("No message available")
}일반적인 패턴은 select를 time.After를 사용한 타임아웃과 결합하는 것입니다. time.After는 지정된 시간이 지난 후 값을 수신하는 채널을 반환합니다:
select {
case result := <-ch:
fmt.Println("Got result:", result)
case <-time.After(2 * time.Second):
fmt.Println("Timeout!")
}이는 채널 연산이 완료되지 않을 수 있는 상황에서 프로그램이 영원히 대기하는 것을 방지합니다.
직접 해보기
package main
import (
"fmt"
)
func main() {
var mode string
fmt.Scanln(&mode)
var serviceName string
fmt.Scanln(&serviceName)
var serviceStatus string
fmt.Scanln(&serviceStatus)
// TODO: 다양한 모드 처리: "primary", "backup", "stop", "check"
// "primary", "backup", "stop" 모드의 경우:
// - 세 개의 채널 생성: primary, backup, done
// - 적절한 채널로 데이터를 보내는 고루틴 실행
// - Monitor를 호출하고 결과 출력
// "check" 모드의 경우:
// - 추가 플래그 읽기 ("send" 또는 "nosend")
// - 버퍼 용량이 1인 채널 생성
// - 플래그가 "send"인 경우, 채널에 업데이트 전송
// - CheckStatus를 호출하고 결과 출력
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍의 모든 레슨
8에러 처리와 OOP
error 인터페이스사용자 정의 에러 타입에러 래핑 (fmt.Errorf)센티넬 에러errors.Is()와 errors.As()Panic, Defer, Recover요약 - 파일 파서