channel은 채널 연산지인 <-를 통해 값을 주고 받을 수 있는 하나의 분리된 통로입니다. Go의 핵심 가치관인 CSP스타일의 Concurrent 프로그래밍을 지원하기위해 제공되며, Goroutine간 데이터를 전달하는 역할로 사용됩니다.
ch <- v // channel ch에 v를 전송한다.
v := <- ch // ch로 부터 값을 받고, 값을 v에 대입한다.
//데이터는 화살표의 방향대로 흐릅니다.
channel은 map과 slice처럼 사용하기 전에 생성되어야만 합니다.
채널은 make()를 통해 생성할 수 있습니다.
ch := make(chan int)
기본적으로 전송과 수신은 다른 한 쪽이 준비될 때까지 block상태입니다.
이는 명시적인 lock이나 조건 변수 없이 goroutine이 synchronous하게 작업될 수 있도록 합니다.
예제 코드는 두개의 goroutine에 작업을 분산시키면서 slice 있는 숫자들을 더합니다. 두 goroutine이그들의 연산을 완료하면 최종 결과를 계산합니다.
package main
import (
"fmt"
)
func sum(s []int, c chan int) {
sum := 0
for _, v := range s {
sum += v
}
c <- sum // send sum to c
}
func main() {
s := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(s[:len(s)/2], c)
go sum(s[len(s)/2:], c)
x, y := <-c, <-c // receive from c
fmt.Println(x, y, x+y)
}
먼저 호출한 sum에서 c에 저장한 값이 7 + 2 + 8 이므로 17
그 다음 호출한 sum에서 c에 저장한 값이 -9 + 4 + 0이므로 -5
그 둘을 더한값이 17 -5 = 12이므로 17, -5, 12가 출력될 것이라 예상했으나
-5가 먼저출력되었다.
go sum(s[:len(s)/2], c)
time.Sleep(100 * time.Millisecond)
go sum(s[len(s)/2:], c)
위처럼 코드를 수정하였더니
예상한 출력값이 나왔다.
sum 함수를 아래와 같이 수정해보았다.
func sum(s []int, c chan int) {
sum := 0
for _, v := range s {
sum += v
fmt.Println(v)
}
c <- sum // send sum to c
}
...
go sum(s[:len(s)/2], c)
go sum(s[len(s)/2:], c)
실행 자체가 두번째 sum이 먼저 되었다. 그 이유를 아시는 분은 알려주시면 감사하겠습니다.
Channel은 bufferd될 수 있습니다.
즉, Channel이 버퍼를 가질 수 있다는 의미입니다.
bufferd channel을 초기화 하기 위해선 make의 두번째 인자로 buffer 길이를 제공하면 됩니다.
ch := make(chan int, 100)
buffer channel로 전송은 그 buffer의 사이즈가 다 찼을 경우에만 block됩니다.
package main
import "fmt"
func main() {
ch := make(chan int, 1)
ch <- 1
ch <- 2
fmt.Println(<-ch)
fmt.Println(<-ch)
}
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.main()
/tmp/sandbox1722270452/prog.go:8 +0x4b
Program exited: status 2.
버퍼의 크기가 1로써 버퍼가 가득 차 block상태가 되어 더이상 값을 넣을 수 없다.
package main
import "fmt"
func main() {
ch := make(chan int, 1)
ch <- 1
k := <- ch
ch <- 2
fmt.Println(<-ch)
fmt.Println(k)
}
먼저 넣은 1을 k에 빼주고 2를 넣으므로써 ch buffer의 크기를 넘지 않도록 하였다.
buffer로 부터의 수신은 그 buffer가 비어잇을 때 block됩니다.
package main
import "fmt"
func main() {
ch := make(chan int, 1)
ch <- 1
k := <- ch
q := <- ch
ch <- 2
fmt.Println(<-ch)
fmt.Println(k)
fmt.Println(q)
}
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan receive]:
main.main()
/tmp/sandbox4180547330/prog.go:9 +0x74
Program exited: status 2.
9번째 줄 q := <- ch에서 에러가 발생했다.
왜냐하면, 이미 ch의 버퍼는 비어있어 block되어있기 때문이다.