반응형

 go에서 channel은 채널 연산자인 <-를 통해서 데이터를 주고 받는 통로로 사용됩니다.

 주로 goroutines 사이에서 데이터를 주고 받는데 상대편이 준비될 때까지 채널에서 대기함으로써 Lock을 사용하지

 않고 데이터를 동기화 할 수 있습니다.

func main() {
    // string형 채널을 생성합니다.
    channel := make(chan string)

    // goroutines을 실행
    go func(str string) {
        // 전달 받은 변수를 수정해서 channel에 전달
        channel <- "My name is " + str
    }("Ahn")

    // channel을 통해서 데이터를 전달 받습니다.
    result := <-channel

    println(result)
}

 위의 예제를 보면 string channel타입의 channel 변수를 선언한 후 goroutine과 main함수 사이에서 데이터를 

 주고 받는 모습을 볼수 있습니다.

채널 연산자 사용법 설  명
[채널] <- [값]

채널에 값을 Send 합니다. 

채널 Send연산자 호출 대상은 channel에 종류에 따라서 다르게 동작합니다. 

만약 unbuffered 채널이라면 수신자가 받을 때까지 대기합니다. 

buffered 채널이고 공간이 남아 있다면 값 복사 비용만큼만 대기합니다. 

[값] = <-[채널]

채널에서 값을 Recv 합니다.

채널 Recv연산자 호출 대상은 언제나 데이터를 들어 올 때까지 대기하게 됩니다. 

Channel의 버퍼링 기능

 channel을 생성할 때 buffered와 unbuffered channel로 생성할 수 있습니다. 위의 예제의 channel은 unbuffered로

 생성되었습니다. 이름과 같이 저장공간이 없기 때문에 송신자(Sender)는 수신자(Reciever)가 데이터를 받을 때까지

 대기합니다. buffered channel은 선언된 저장 공간만큼 송신자는 데이터를 보내고 다른 작업(비동기)을

 계속 할 수 있습니다.

// unbuffered 생성
channel := make(chan string)
// 10개의 buffer를 가진 channel생성
channel := make(chan string, 10)

 buffered channel을 생성하는 방법은 make 함수에 두번째 인자에 버퍼의 갯수을 전달 하는 것입니다.

Channel의 파라미터

 channel또한 함수의 파라미터로 전달 할 수 있는데 default로 송수신이 가능한 채널로 전달하지만

 송신또는 수신만 가능한 channel로 전달 할 수 있습니다. 

//OnlySendChannel 함수는 인자로 송신만 가능한 채널을 전달합니다.
func OnlySendChannel(ch chan<- string){
    // 채널에 string을 송신함
    ch <- "Only Send ch"
    // error 발생 ch에서 수신 못함!!
    <- ch
}
// OnlyRecvChannel 함수는 인자로 수신만 가능한 채널을 전달합니다. 
func OnlyRecvChannel(ch <-chan string){
	// 채널에 string을 수신함
    value := <- ch 
    println(value)
    
    // error 채널에 string을 송신 못함
    ch <- "receive complete"
}

 위의 예제를 실행 시켜보면 error를 발생시키는 것을 볼수 있습니다. 이렇게 파라미터를 지원함으로써

 조금 더 안전한 channel의 사용이 가능합니다. 

Channel Range와 Close

 송신자(sender)는 더 이상 데이터를 보낼 것이 없으면 channel을 close 할 수 있습니다.

 수신자(receiver)는 receive 표현식의 두번째 파라미터를 통해서 channel이 닫힌 상태인지 확인 할 수 있습니다.

 (주의 수신자는 채널을 닫으면 안됩니다)

 (채널은 파일과 달리 굳이 닫을 필요는 없지만 수신자에 닫힘을 알려야 할 때 close를 호출합니다.)

// ok 변수는 채널이 상태를 표현합니다. 
v, ok := <-ch

 예제에서 ok 변수를 통해서 채널의 닫힌 여부를 확인 할 수 있습니다. 더이상 수신할 값이 없으며 channel이

 닫혀있다면 ok가 false입니다. 

func fibonacci(n int, c chan int) {
	x, y := 0, 1
	for i := 0; i < n; i++ {
		c <- x
		x, y = y, x+y
	}
	close(c)
}

func main() {
	c := make(chan int, 10)
	go fibonacci(cap(c), c)
	for i := range c {
		fmt.Println(i)
	}
}

 "for i := range <채널변수>" 를 사용하면 채널이 닫힐 때까지 채널에서 반복적으로 값을 받을 수 있습니다. 

Channel Select 

 go에서 select문은 복수의 채널들을 기다리면서 준비상태가 된 채널을 실행하는 기능을 가지고 있습니다. 

 select 구문은 case중에 하나가 실행 될 수 있을 때까지 block한 다음에 해당 case를 실행합니다.

 만약에 여러개가 준비되었다면 무작위로 하나를 선택합니다.

func fibonacci(c, quit chan int) {
	x, y := 0, 1
	for {
		select {
		case c <- x:
			x, y = y, x+y
		case <-quit:
			fmt.Println("quit")
			return
		}
	}
}

func main() {
	c := make(chan int)
	quit := make(chan int)
	go func() {
		for i := 0; i < 10; i++ {
			fmt.Println(<-c, " i = ", i)
			if i == 8 {
				quit <- 1
			}
		}
		quit <- 0
	}()
	fibonacci(c, quit)
}

결과 화면

 만약에서 select 문에 default 문이 존재하면 case문이 준비되지 않더라도 계속 대기하지 않고 바로 default문을 

 실행합니다. 

func main() {
	// 200밀리초 이후에 켜집니다.
	tick := time.Tick(200 * time.Millisecond)
    // 500밀리초 이후에 켜집니다.
	boom := time.After(500 * time.Millisecond)

	StartTime := time.Now()

	for {
		select {
		case <-tick:
			fmt.Println("on tick time")
		case <-boom:
			fmt.Println("on BOOM!")
			return
		default:
        	// case가 준비 되지 않았다면 시간을 찍고 100 밀리초 대기합니다.
			t := time.Now()
			fmt.Println(t.Sub(StartTime).Milliseconds(), "over millisec.")
			time.Sleep(100 * time.Millisecond)
		}
	}
}

결과 화면

반응형

+ Recent posts