채널 사용하기


이재홍 http://www.pyrasis.com 2014.12.17 ~ 2015.02.07



채널 버퍼링

이번에는 채널의 버퍼링에 대해 알아보겠습니다. 다음은 채널의 버퍼가 가득차면 값을 꺼내서 출력합니다.

  • make(chan 자료형, 버퍼_개수)
package main

import (
	"fmt"
	"runtime"
)

func main() {
	runtime.GOMAXPROCS(1)

	done := make(chan bool, 2) // 버퍼가 2개인 비동기 채널 생성
	count := 4                 // 반복할 횟수

	go func() {
		for i := 0; i < count; i++ {
			done <- true                // 채널에 true를 보냄, 버퍼가 가득차면 대기
			fmt.Println("고루틴 : ", i) // 반복문의 변수 출력
		}
	}()

	for i := 0; i < count; i++ {
		<-done                         // 버퍼에 값이 없으면 대기, 값을 꺼냄
		fmt.Println("메인 함수 : ", i) // 반복문의 변수 출력
	}
}

채널에 버퍼를 1개 이상 설정하면 비동기 채널(asynchronous channel)이 생성됩니다. 비동기 채널은 보내는 쪽에서 버퍼가 가득 차면 실행을 멈추고 대기하며 받는 쪽에서는 버퍼에 값이 없으면 대기합니다.

고루틴을 생성하고, 반복문을 실행할 때마다 채널 donetrue 값을 보냅니다.

go func() {
	for i := 0; i < count; i++ {
		done <- true                // 채널에 true를 보냄, 버퍼가 가득차면 대기
		fmt.Println("고루틴 : ", i) // 반복문의 변수 출력
	}
}()

비동기 채널이므로 버퍼가 가득 찰때까지 값을 계속 보냅니다. 여기서는 채널의 버퍼를 2개로 설정했으므로 donetrue를 2번 보낸 뒤 그 다음 루프에서 대기합니다(버퍼가 가득 차면 대기하므로 i가 0, 1일 때 채널에 값을 보낸 뒤 i가 2일때 done <- true에서 대기합니다).

이제 메인 함수에서는 반복문을 실행할 때마다 채널 done에서 값을 꺼냅니다.

for i := 0; i < count; i++ {
	<-done                         // 버퍼에 값이 없으면 대기, 값을 꺼냄
	fmt.Println("메인 함수 : ", i) // 반복문의 변수 출력
}

비동기 채널에 버퍼가 2개이므로 done에는 이미 값이 2개 들어있습니다. 따라서 루프를 두 번 반복하면서 <-done에서 값을 꺼냅니다. 그 다음에는 채널이 비었으므로 실행을 멈추고 대기합니다. 그리고 다시 고루틴 쪽에서 값을 두 번 보내고, 메인 함수에서 두 번 꺼냅니다. 즉 고루틴 → 고루틴 → 메인 함수 → 메인 함수 순서로 실행됩니다.

실행 결과

고루틴 :  0
고루틴 :  1
메인 함수 :  0
메인 함수 :  1
고루틴 :  2
고루틴 :  3
메인 함수 :  2
메인 함수 :  3


그림 34-4 비동기 채널과 버퍼

이번 예제에서는 채널에 값을 연달아서 보내고 꺼냈지만 시간차를 두고 보내거나 꺼낼 수도 있습니다. 이렇게 되면 채널에서 값을 꺼냈을 때 버퍼에 공간이 생기므로 다른 쪽에서 다시 값을 보낼 수 있습니다. 버퍼 개수를 수정하고, 고루틴과 메인 함수의 반복문에서 time.Sleep 함수를 사용하여 여러 가지 형태로 실행해보기 바랍니다.

이번 예제에서는 runtime.GOMAXPROCS(1)를 설정하여 CPU 코어를 하나만 사용합니다. CPU 코어를 여러 개 사용하면 결과가 달라질 수 있는데, 여러 곳에서 채널에 값을 보내고 꺼내기 때문입니다. 그러므로 멀티코어에서 비동기 채널을 사용할 때는 실행 순서나 채널 사용 방법에 좀 더 신경써야 합니다.

추가 해설
Go 1.4에서 채널 버퍼링 예제를 실행하면 고루틴 2번, 메인 함수 2번씩 출력됩니다. 하지만 Go 1.5 이상부터는 고루틴 스케쥴링 구현이 바뀌었기 때문에 이렇게 출력되지 않을 수 있습니다. 단, 비동기 채널의 구현과 동작은 바뀐 것이 없으므로 보내는 쪽에서 버퍼가 가득 차면 실행을 멈추고 대기하며 받는 쪽에서는 버퍼에 값이 없으면 대기한다는 점만 기억하면 됩니다. 관련 내용은 Go 1.5 릴리스 노트 Runtime 부분을 참조하세요.


저작권 안내

이 웹사이트에 게시된 모든 글의 무단 복제 및 도용을 금지합니다.
  • 블로그, 게시판 등에 퍼가는 것을 금지합니다.
  • 비공개 포스트에 퍼가는 것을 금지합니다.
  • 글 내용, 그림을 발췌 및 요약하는 것을 금지합니다.
  • 링크 및 SNS 공유는 허용합니다.

Published

01 June 2015