go - reflect 패키지를 이용한 동적 select문
익명(211.109)
2017-09-12 00:53
추천 0
go에서는 여러개의 채널에서 넘어오는 데이터들을 처리하기 위해 select문을 종종 쓰게 되는데, 때로는 채널들이 컴파일 이전에 확정되지 않고 동적으로 생성되는 상황이 생긴다. 그러면 select문도 이에 맞춰 동적으로 케이스가 생성되어야 하는데, 빌트인 select로는 불가능하다. 이를 위해 reflect 패키지에서는 동적 select를 따로 준비해뒀다.
package main
import (
"fmt"
"reflect"
)
func main() {
// 채널들(본래는 컴파일 시점에서 알 수 없는 개수만큼 만드는 코드여야 의미가 있겠지만 넘어가자)
var chs []chan int
for i := 0; i < 5; i++ {
ch := make(chan int)
go func(ch chan int) {
for i := 0; i < 10; i++ {
ch <- i
}
close(ch)
}(ch)
chs = append(chs, ch)
}
// 채널의 개수와 동일한 개수의 케이스들
var selectCases []reflect.SelectCase
for i := 0; i < 5; i++ {
selectCases = append(selectCases, reflect.SelectCase{
Dir: reflect.SelectRecv, // 채널에서 값을 받아오는 케이스
Chan: reflect.ValueOf(chs[i]), // 같은 번호의 채널에서 받아오기
})
}
// 모든 채널이 닫혔다면 for 루프 탈출하기 위해 카운터 만들기
var cnt int
for cnt < 5 {
// no : 몇번째 케이스인가?
// rv : 채널에서 받은 값(물론 reflect.Value의 형태)
// ok : 열린 채널인가?
no, rv, ok := reflect.Select(selectCases)
if ok {
fmt.Printf("Channel %d : Value %d\n", no, rv.Int())
} else {
cnt++
// 정적 select에서와 마찬가지로 닫힌 채널은 nil로 바꿔 제로 밸류를 받아오는 것을 방지하자
selectCases[no].Chan = reflect.ValueOf(nil)
}
}
}
위 예시처럼 채널/케이스가 5쌍이라고 딱 정해서 쓸거면 동적으로 짤 필요가 없지만, 상황을 만들기가 귀찮아서 그런 거니 그냥 넘어가자...... 그리고 이런 방법이 있다는 소개일 뿐, 리플렉션은 성능 저하의 주범이니, 안 쓰고는 정말로 답이 안 나오는 상황 아니면 쓰지 말자.
댓글 0