채널은 make() 함수로 만들 수 있으며, 채널 연산자 <-를 통해 데이터를 주고 받는다.
채널은 보통 goroutine들 사이 데이터를 주고 받을 때 사용되는데, 별도의 lock은 필요없이 데이터를 동기화할 수 있다.
채널을 생성할 때는 어떤 타입의 데이터를 보낼건지 타입을 지정해주어야 한다.
채널은 송신자와 수신자가 서로를 기다리기 때문에 이를 이용하여 goroutine 이 끝날 때까지 기다리는 기능을 구현할 수 있다.
func main () {
done := make (chan bool)
go func() {
for i :=0; i<10; i++ {
fmt.Println(i)
}
done <- true
}()
<-done
}
Go 채널은 Unbuffered Channel과 Buffered Channel이 있다.
위의 예제에서는 Unbuffered Channel로서 이 채널에서는 하나의 수신자가 데이터를 받을 때까지 송신자가 데이터를 보내는 채널에 묶여 있는다.
하지만 Buffered Channel은 수신자가 받을 준비가 되어 있지 않더라도 지정된 버퍼만큼 데이터를 보내고 계속 다른 일을 수행할 수 있다.
버퍼 채널은 make(chan type, N) 함수를 통해 생성되는데, 두번째 파라미터 N에 사용할 버퍼 갯수를 넣는다.
예를 들어, make(chan int, 10)은 10개의 정수형을 갖는 버퍼 채널을 만든다.
만약 수신자 go루틴이 없는데 버퍼 채널을 사용하지 않으면, 데드락이 발생한다.
하지만 버퍼 채널을 사용하면, 수신자가 당장 없더라도 최대 버퍼 수까지 데이터를 보낼 수 있으므로 에러를 발생하지 않는다.
함수의 파라미터로 채널을 전달할 때, 해당 채널로 송신할 것인지 수신할 것인지 지정할 수 있다.
송신 파라미터는 (p chan <- int) 와 같이 chan <-를 사용하고, 수신 파라미터는 (p <- chan int) 와 같이 <-chan을 사용한다.
채널을 생성하고 데이터를 송신한 후, close() 함수를 사용하여 채널을 닫을 수 있다.
닫힌 채널로의 송신은 불가능하지만, 수신은 가능하다.
채널 수신에 사용되는 <-ch 는 두 개의 리턴 값을 갖는데, 첫째는 채널 메시지고, 두번째는 수신여부 (boolean)이다.
채널에서 송신자가 송신한 후 채널을 닫을 수 있고, 수신자는 임의의 갯수만큼의 데이터를 채널이 닫힐 때까지 수신할 수 있다.
Go 의 select 문은 복수 채널들을 기다리면서 준비된 채널을 실행하는 기능을 제공한다.
즉, select문은 여러 개의 case문에서 각각 다른 채널들을 기다리다가 준비된 채널 case만 실행하는 것이다.
만약 복수 채널에서 신호가 오면 Go 런타임이 랜덤하게 그 중 한 개를 선택한다. 하지만 select문에 default문이 있으면, case문 채널이 준비되지 않더라도 계속 대기하지 않고 바로 default문을 실행한다.
func main () {
done1 := make(chan bool)
done2 := make(chan bool)
go run1(done1)
go run2(done2)
EXIT:
for {
select {
case <- done1:
fmt.Println("run1 완료")
case <- done2:
fmt.Println("run2 완료")
break EXIT
}
}
}
func run1 (done chan bool) {
time.Sleep(1 * time.Second)
done <- true
}
func run2 (done chan bool) {
time.Sleep(2 * time.Second)
done <- true
}