Channel
Channel 이란?
고루틴을 사용하는 데 있어서 데이터를 전달하는 가장 효과적인 방법이다. 데이터를 전달하는데 통로 역할을 한다. 채널을 사용하는 데 있어 서로 간에 많은 정보를 알 필요는 없으며 채널에 대한 주소 값만 알면 채널을 통해 데이터를 송신하고 수신할 수 있다.
Channel의 종류
- 양방향 채널 (chan interface{})
데이터를 송신하고 수신하는 역할 모두 가능하다. - 송신용 채널 (chan <- interface{})
데이터를 송신하는 역할만이 가능하다. - 수신용 채널 (<- chan interface{})
데이터를 수신하는 역할만이 가능하다. - nil
인스턴스화가 진행되지 않고 선언만이 진행된 상태이다.
한 가지 역할만을 하는 단방향 채널은 화살표의 방향이 채널을 가리키면 채널에게 값을 전달하는 송신용, 채널에서 화살표가 나온다면 채널을 통해 값을 받는 수신용으로 생각하면 좋을 듯하다.
Channel의 생성
var testChan chan interface{}
testChan = make(chan interface{})
다른 값들과 마찬가지로 := 를 통해 선언과 초기화를 동시에 진행하는 것이 가능하며 예제와 같이 분리하여 나누는 것도 가능하다.
또한 양방향으로 채널을 선언하였지만 단방향 채널로 인스턴스화 하는 것도 가능하다.
var testChan chan interface{}
testChan = make(chan<- interface{})
이런 식으로 말이다.
Channel의 사용 예시
예시 코드
package main
import "fmt"
func main() {
stringChan := make(chan string)
go func() {
stringChan <- "Hello World!"
}()
fmt.Println(<-stringChan)
}
실행 결과
Hello World!
어쩌면 의아함을 가질 수 있는 결과이다. 분명 고루틴을 위와 같은 방법을 통해 실행시켰을 때 메인 고루틴이 먼저 종료가 되어 익명 고루틴이 실행되지 못했던 경험을 했었을 수 있으니. 그러나 채널은 블락 형태로 진행되기에 이 부분에서 확실히 실행을 기대할 수 있다. 이 부분은 나의 이전 포스팅을 살펴보면 도움이 될 것이다.
Channel의 close
예시 코드
package main
import "fmt"
func main() {
stringChan := make(chan string)
go func() {
stringChan <- "Hello World!"
}()
val, ok := <-stringChan
fmt.Printf("%v : %v\n", val, ok)
close(stringChan)
val, ok = <-stringChan
fmt.Printf("%v : %v", val, ok)
}
실행 결과
Hello World! : true
: false
채널 값을 수신할 때에는 두 개의 값을 return 하는데 채널을 통해 수신한 값과 현재 채널의 상태가 그것이다. 예제에서 볼 수 있듯 채널이 닫힌 상태에서도 값을 읽을 수 있으며 이때에 수신받는 값은 타입의 기본값이다(여기선 "" - 빈 스트링).
채널의 close를 통해 다양한 패턴을 사용할 수 있다.
for문을 통해 chan을 읽는 것이 한 예이다.
for문을 사용하는 예시 코드
package main
import "fmt"
func main() {
intChan := make(chan int)
go func() {
defer close(intChan)
for i := 0; i < 5; i++ {
intChan <- i
}
}()
for i := range intChan {
fmt.Printf("%d ", i)
}
}
실행 결과
0 1 2 3 4
정상적으로 실행되고 원하는 결괏값을 얻는 것을 볼 수 있다.
만일 채널을 close 하지 않는다면
fatal error: all goroutines are asleep - deadlock!
데드락에 걸리는 모습을 볼 수 있을 것이다.
또한 대기 중인 채널 모두에게 대기를 해제하게 할 수도 있다.
대기 해제 예시 코드
package main
import (
"fmt"
"sync"
)
func main() {
begin := make(chan interface{})
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
<-begin
fmt.Printf("%v has begun\n", i)
}(i)
}
fmt.Println("Unblocking goroutines....")
close(begin)
wg.Wait()
}
실행 결과
Unblocking goroutines....
0 has begun
2 has begun
3 has begun
4 has begun
1 has begun
고루틴은 <-begin 부분에서 대기를 진행하고 있다. 그렇기에 고루틴이 닫히기 전엔 어떠한 동작도 하지 않는다. 그리고 채널이 닫히면 값을 출력하는 것을 볼 수 있다.
마무리
채널..
고루틴을 어떻게 활용하는가에 가장 큰 역할을 하는 친구 같다.
이 친구를 정복해야 Go를 잘 사용한다고 말할 수 있지 않을까 싶다.
'Study > Go' 카테고리의 다른 글
[Go] 고루틴(GoRoutine) 심화(6) - 고루틴 에러 처리 (0) | 2022.06.20 |
---|---|
[Go] 고루틴(GoRoutine) 심화(5) - 누수 관리 (0) | 2022.05.24 |
[Go] 고루틴(GoRoutine) 심화(3) - sync.Pool (0) | 2022.05.13 |
[Go] 고루틴(GoRoutine) 심화(2) - 클로저 함수, 메모리 (0) | 2022.04.28 |
[Go] 고루틴(GoRoutine) 심화(1) - 합류지점 (0) | 2022.04.16 |