[Go] 고루틴/채널/대기 그룹
Updated:
개요
- 고루틴(goroutine)
- 쓰레드와 유사
- 쓰레드보다 적은 메모리 사용
- go 키워드 사용
- 채널(channel)
- 고루틴간의 데이터 송수신 및 동기화
- chan 타입 사용
- 버퍼링(buffering)
- unbuffered channel
channel := make(chan int)
- 수신하기전까지 송신 채널이 차단
- buffered channel
channel := make(chan bool, 10)
- 지정된 크기의 버퍼가 다 채워지기 전까지 송신 채널이 차단되지 않음
- unbuffered channel
- 송신
channel <- 1
- 수신
<-channel
- 송신될 때까지 차단
- 방향(directions)
- 기본적으로는 양뱡향
- make 시 단방향 채널 생성 가능
- 파라미터를 통해 양뱡향 채널을 단방향 채널로 변환 가능
- 단방향 채널을 양뱡향 채널 파라미터로 전달하거나 송(수)신 채널을 수(송)신 채널 파라미터로 넘기면 컴파일 에러 발생
- 송신
chan <- int
- 수신
<-chan int
- 닫기
- close 함수 사용
- 호출 이후 송신은 불가능, 수신은 가능
- 채널이 닫힐 떄까지 수신하는 방법
- 방법 1
for { if data, ok := <-channel; ok { log.Println("receive data : ", data) } else { break } }
- 방법 2
for data := range channel { log.Println("receive data : ", data) }
- 방법 1
- select
- n개의 채널을 수신 대기
- switch문과 유사하게 case/default문 등을 사용
- 준비된 채널이 없을 경우 default문이 없으면 대기, 있으면 default문 수행
- 대기 그룹(WaitGroup)
- 고루틴들의 대기에 사용
- Add
- 대기 수 증가
- Done
- 대기 수 감소
- Wait
- 대기 수가 0이 될 때까지 대기
예제
- 코드
package main import ( "log" "sync" "time" ) func bufferingJob(channel chan int, dataCount int) { wgSend := sync.WaitGroup{} for i := 0; i < dataCount; i++ { wgSend.Add(1) go func(channel chan int, wg *sync.WaitGroup, data int) { defer wg.Done() channel <- data log.Println("send data : ", data) }(channel, &wgSend, i) } time.Sleep(time.Second) wgReceive := sync.WaitGroup{} wgReceive.Add(1) go func(channel chan int, wg *sync.WaitGroup) { defer wg.Done() /* for { if data, ok := <-channel; ok { log.Println("receive data : ", data) } else { break } } */ for data := range channel { log.Println("receive data : ", data) } }(channel, &wgReceive) wgSend.Wait() close(channel) wgReceive.Wait() } func unbufferedChannelTest() { channel := make(chan int) bufferingJob(channel, 3) } func bufferedChannelTest() { channel := make(chan int, 3) bufferingJob(channel, 5) } func directionsTest() { channel := make(chan int, 1) func(channel chan<- int) { channel <- 1 }(channel) func(channel <-chan int) { println(<-channel) }(channel) } func selectTest() { channel1 := make(chan int) channel2 := make(chan bool) go func() { count := 0 EXIT: for { select { case data := <-channel1: println(data) case data := <-channel2: println(data) break EXIT default: count++ } } println("default call : ", count) }() channel1 <- 1 channel1 <- 2 channel2 <- true } func main() { unbufferedChannelTest() println("------") bufferedChannelTest() println("------") directionsTest() println("------") selectTest() }
- 실행 결과
2022/12/14 02:16:27 receive data : 0 2022/12/14 02:16:27 receive data : 1 2022/12/14 02:16:27 receive data : 2 2022/12/14 02:16:27 send data : 2 2022/12/14 02:16:27 send data : 0 2022/12/14 02:16:27 send data : 1 ------ 2022/12/14 02:16:27 send data : 4 2022/12/14 02:16:27 send data : 0 2022/12/14 02:16:27 send data : 1 2022/12/14 02:16:28 send data : 2 2022/12/14 02:16:28 receive data : 4 2022/12/14 02:16:28 receive data : 0 2022/12/14 02:16:28 receive data : 1 2022/12/14 02:16:28 receive data : 2 2022/12/14 02:16:28 receive data : 3 2022/12/14 02:16:28 send data : 3 ------ 1 ------ 1 2 true default call : 256