Golang 채널 - Go series(14)

Date:     Updated:

Categories:

Tags: ,

image

채널

채널은 데이터를 주고받는 곳이다. make()를 이용하여 생성이 되어야 하고 채널 연산자 <-를 따로 사용한다. 흔히 고루틴 사이에 데이터를 전송하기 위해서 사용한다. 별도의 lock이 필요없이 상대편이 필요할 때 까지 채널에서 대기한다.

package main

func main(){
    ch := make(chan int) // chan은 채널, int는 정수형, 즉 정수형 채널 생성
    go func(){
        ch<-12345
    }()
    num := <- ch
    println(num)
}

또한 일종의 채널의 속성으로 Go 루틴이 끝날 때 까지 기다리는 기능을 구현할 수 있다.

func main(){
    done := make(chan bool)
    go func(){
        for i := 0; i<10; i++{
            println(i)
        }
        done<-true
    }()
    <-done
}

채널 버퍼링

위에서의 예제는 Unbuffered Channel(하나의 수신자가 데이터를 받을 때 까지 묶임) 이지만,
Bufferend Channel도 있다. 이는 make(chan type, N)을 통해 생성되는데, N에는 사용할 버퍼의 개수를 입력한다.
아래 예제는 에러가 발생한다.

func main(){
    channel := make(chan int)
    channel <- 123 // (2)
    println("히히힝")
}

이는 수신하는 고루틴이 없어서 (2) 부분에서 흐름이 멈추기 때문이다. 하지만 버퍼드 채널은 어떨까

func main(){
    channel := make(chan int, 1)
    channel <- 123
    println(<-channel)
}

이는 channel 자체에 하나의 버퍼가 내장되어있어서, 하나를 저장하고도 다음 차례로 넘길 수가 있게 되어 에러가 없는 것이다.

채널 파라미터

채널에는 파라미터도 존재할 수 있다. 일반적으로 송수신을 모두 하는 채널을 전달하지만, 특별히 해당 채널로 통신을 할 것인지 혹은 수신할 것인지를 지정할 수도 있다. 송신 파라미터는 p chan <- int와 같이 chan <-을 사용하고 수신은 p <- chan int와 같이 사용한다.

func main() {
	ch := make(chan int,1)
	sendChan(ch)
	receiveChan(ch)
}

func sendChan(ch chan <- int) {
	ch <- 123
}

func receiveChan(ch <- chan int) {
	data := <-ch
	println(data)
}

채널 close

close 함수를 이용하여 채널을 닫을 수 있는데, 더이상 송신은 안되지만 남은 버퍼만큼의 수신은 가능하다. 또한 리턴값이 두 개인데, 두 개중 두 번째로 리턴되는 값이 수신 true/false를 나타낸다.

func main(){
    ch:= make(chan int, 2)
    ch <- 1
    ch <- 2

    close(ch)
    println(<-ch)
    println(<-ch)

    if _, isSuccess := <-ch; !isSuccess{
        println("X")
    }
}

range문을 사용한 채널

다음과 같이 사용하면, 채널이 닫히면 자동적으로 루프는 사라진다.

func main(){
    ch:= make(chan int, 2)
    ch <- 1
    ch <- 2

    close(ch)

    for i := range ch {
        println(i)
    }
}

select 문

복수 채널들을 기다리면서 준비된 채널을 실행하는 기능을 제공한다. 여러개의 case 문에서 각각 다른 채널을 기다리다가 준비가 된 채널 case를 실행하는 것이다. 채널이 준비되지 않으면 계속 대기하게 되며 가장 먼저 도착한 채널의 case를 진행한다. default문이 있다면 계속 대기하지는 않는다.

import "time"

func main(){
    isDone1 := make(chan bool)
    isDone2 := make(chan bool)
    go firstRoutine(isDone1)
	go secondRoutine(isDone2)
	EXIT:
		for{
			select {
			case <-isDone1:
				println("1은 끝")
			case <-isDone2:
				println("2도 끝")
				break EXIT
			default:
				println("아직임")
			}
		}
}

func firstRoutine(ch chan bool){
    time.sleep(1 * time.Second)
    ch <- true
}

func secondRoutine(ch chan bool){
    time.sleep(2 * time.Second)
    ch <- true
}

Leave a comment