Goroutine이 다중 커뮤니케이션 연산에서 대기할 수 있게 합니다.
select는 케이스 중 하나가 실행될 수 있을 때까지 block합니다.
그 후 해당 케이스를 실행합니다.
여러 개가 준비되면 무작위로 하나를 선택합니다.
package main
import "fmt"
func fibonacci(c, quit chan int) {
x, y := 0, 1
for { //중지 조건이 없는 while문
select {
case c <- x: //채널 c로 보낼 수 있고
x, y = y, x+y
case <-quit: //quit에 값이 들어왔을 때 해당 case 실행
fmt.Println("quit")
return
}
}
}
func main() {
//2 channel 모두 버퍼링이 없기때문에
//누군가 수신을 시도할 때만 채널로 보낼 수 있습니다.
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 10; i++ {
fmt.Print(<-c, " ")
}
quit <- 0
}()
fibonacci(c, quit)
}
fibonacci(c, quit) 이 함수를 호출하지 않으면 아무런 출력도 되지 않습니다.
func() 내부에 fmt.Print(<-c)가 어떤식으로 인식되어 select되는지 모르겠어서 찾아보았습니다.
slect문의 내용을 해석하면 다음과 같습니다.
select {
case c <- x: // c에 send 할 수 있을 때
// update my variables
x, y = y, x+y
case <-quit: // 만약 quit로부터 값을 받을 수 있다면 exit하겠다.
fmt.Println("quit")
return
}
default case가 없는 의미
"c에 보낼 수 없고 종료에서 읽을 수 없으면 해당case를 만족할 때 까지 block합니다." 를 뜻합니다.
for i:=0; i<10; i++ {
fmt.Println(<-c) // c로 부터 읽는다
}
quit <- 0 // main process를 끝내기 위해 quit에 send한다.
위의 코드가 동작할 수 있는 이유
1. select case를 만족시킬 때까지 block하고 있음
2. goroutine을 사용하여fibonacci(c, quit)
를 분리하여 for 문에서<-c
를 실행해켰을 때 fiboancci함수에서 이를 받아 줄 수 있다.
만약 goroutine으로 분리하지 않는다면?
이와 같은 에러를 만나게 됩니다.
위 코드는 producer/consumer(생산자/소비자) 패턴을 사용하고 있으므로 main()과 go func()이 분리되어 동시에 실행되는 것이 중요한 포인트 입니다.
func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
}()
go fibonacci(c, quit)
위의 코드에서 go 키워드의 위치를 바꾸어 보았다.
fmt.Println구문에서 꺼낼 c값이 없어 에러가 발생하였다.
go fibonacci(c, quit)
func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
}()
이렇게 순서를 바꾸어 보니
출력은 되지만 quit가 출력이 되지 않았다
case <-quit되기전 main goroutine인 func()가 종료되며 그런가? 하고 sleep을 주어보았다.
func main() {
c := make(chan int)
quit := make(chan int)
go fibonacci(c, quit)
func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
}()
time.Sleep(1)
}
예상대로 quit 출력이 잘 되었다.
두 함수 모두에 goroutine을 적용하면 어떻게 될까?
go fibonacci(c, quit)
go func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
}()
아무런 출력도 나오지 않았다
예시코드 수정 3처럼 sleep을 걸어주니 그제야 출력이 되었다.
역시 go로 돌려서 main goroutine이 바로 끝나버렸기 때문이다.