채널 사용하기

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

보내기 전용 및 받기 전용 채널 사용하기

이번에는 보내기 전용 및 받기 전용 채널에 대해 알아보겠습니다. 보내기 전용 채널과 받기 전용 채널을 그림으로 표현하면 다음과 같습니다.


그림 34-5 보내기 전용 채널 및 받기 전용 채널

보내기 전용 채널과 받기 전용 채널은 값의 흐름이 한 방향으로 고정된 채널입니다.

다음은 0부터 4까지 채널에 값을 보내고, 다시 채널에서 값을 꺼내서 출력합니다. 그리고 반복문이 끝난 뒤 채널에 100을 보낸 뒤 다시 꺼내서 출력합니다.

package main

import "fmt"

func producer(c chan<- int) { // 보내기 전용 채널
	for i := 0; i < 5; i++ {
		c <- i
	}

	c <- 100           // 채널에 값을 보냄

	//fmt.Println(<-c) // 채널에서 값을 꺼내면 컴파일 에러
}

func consumer(c <-chan int) { // 받기 전용 채널
	for i := range c {
		fmt.Println(i)
	}

	fmt.Println(<-c) // 채널에 값을 꺼냄

	// c <- 1        // 채널에 값을 보내면 컴파일 에러
}

func main() {
	c := make(chan int)

	go producer(c)
	go consumer(c)

	fmt.Scanln()
}

실행 결과

0
1
2
3
4
100

보내기 전용 및 받기 전용 채널은 채널 앞 뒤로 <- 연산자를 붙여서 만듭니다. 보통 함수의 매개변수로 사용하거나, 구조체의 필드로 사용합니다.

  • 보내기 전용(send-only): chan<- 자료형 형식입니다. c chan<- int는 int형 보내기 전용 채널 c를 뜻합니다. 보내기 전용 채널은 값을 보낼 수만 있으며 값을 가져오려고 하면 컴파일 에러가 발생합니다.
  • 받기 전용(receive-only): <-chan 자료형 형식입니다. c <-chan int는 int형 받기 전용 채널 c를 뜻합니다. 받기 전용 채널은 range 키워드 또는 <- 채널 형식으로 값을 꺼낼 수만 있으며 값을 보내려고 하면 컴파일 에러가 발생합니다.

chan 키워드를 기준으로 <- (화살표)가 붙은 방향을 보면 보내기 전용인지 받기 전용인지 알 수 있습니다. 즉 chan<-은 chan 키워드로 <-가 들어가므로 보내기 전용, <-chan은 chan 키워드에서 <-가 나오고 있으므로 받기 전용 채널입니다.

여기서는 producer 함수는 매개변수로 보내기 전용 채널을 사용하고, consumer 함수는 매개변수로 받기 전용 채널을 사용합니다. 따라서 producer 함수는 값을 보내기만 하고, consumer 함수는 값을 꺼내기만 합니다.

이번에는 채널을 리턴값으로 사용해보겠습니다. 다음은 두 수를 더한 뒤 채널로 리턴합니다.

package main

import "fmt"

//                    ↓ 함수의 리턴 값은 int 형 받기 전용 채널
func sum(a, b int) <-chan int {
	out := make(chan int) // 채널 생성
	go func() {
		out <- a + b // 채널에 a와 b의 합을 보냄
	}()
	return out           // 채널 변수 자체를 리턴
}

func main() {
	c := sum(1, 2)   // 채널을 리턴값으로 받아서 c에 대입

	fmt.Println(<-c) // 3: 채널에서 값을 꺼냄
}

실행 결과

3

sum 함수는 받기 전용 채널을 리턴하도록 만들었습니다. 채널을 리턴하려면 먼저 make 함수로 채널을 생성합니다. 그리고 고루틴 안에서 채널에 값을 보낸 뒤 고루틴 바깥에서 채널을 리턴합니다.

sum 함수를 사용하여 채널을 리턴값으로 받았으면 <-c처럼 값을 꺼내면 됩니다.

이번에는 채널만 사용하여 값을 더해보겠습니다.

package main

import "fmt"

//                    ↓ 함수의 리턴 값은 int 형 받기 전용 채널
func num(a, b int) <-chan int {
	out := make(chan int) // int형 채널 생성
	go func() {
		out <- a   // 채널에 a의 값을 보냄
		out <- b   // 채널에 b의 값을 보냄
		close(out) // 채널을 닫음
	}()
	return out // 채널 변수 자체를 리턴
}

//            ↓ 함수의 매개변수는 int형 받기 전용 채널
func sum(c <-chan int) <-chan int {
//                        ↑ 함수의 리턴 값은 int형 받기 전용 채널
	out := make(chan int) // int형 채널 생성
	go func() {
		r := 0
		for i := range c { // range를 사용하여 채널이 닫힐 때까지 값을 꺼냄
			r = r + i  // 꺼낸 값을 모두 더함
		}
		out <- r           // 더한 결과를 채널에 보냄
	}()
	return out // 채널 변수 자체를 리턴
}

func main() {
	c := num(1, 2) // 1과 2가 들어있는 채널이 리턴됨
	out := sum(c)  // 채널 c를 매개변수에 넘겨서 모두 더함, 더한 값이 들어있는 out 채널을 리턴

	fmt.Println(<-out) // 3: out 채널에서 값을 꺼냄
}

실행 결과

3

num 함수에서는 숫자 두 개를 받아서 채널에 보낸 뒤 리턴합니다.

func num(a, b int) <-chan int {
	out := make(chan int) // int형 채널 생성
	go func() {
		out <- a   // 채널에 a의 값을 보냄
		out <- b   // 채널에 b의 값을 보냄
		close(out) // 채널을 닫음
	}()
	return out // 채널 변수 자체를 리턴
}

이제 채널에는 숫자 두 개가 저장되어 있습니다. 그리고 close 함수로 채널을 닫아서 range 키워드의 반복이 끝나도록 합니다.

다음과 같이 sum 함수에서는 range 키워드로 채널에서 값을 두 개 꺼내서 모두 더합니다. 그리고 더한 값은 리턴용 채널에 보냅니다.

func sum(c <-chan int) <-chan int {
	out := make(chan int) // int형 채널 생성
	go func() {
		r := 0
		for i := range c { // range를 사용하여 채널이 닫힐 때까지 값을 꺼냄
			r = r + i  // 꺼낸 값을 모두 더함
		}
		out <- r           // 더한 결과를 채널에 보냄
	}()
	return out // 채널 변수 자체를 리턴
}

num 함수가 리턴한 채널에는 1과 2가 들어있습니다. 그리고 이 채널을 sum 함수에 넣어주면 값이 모두 더해집니다. 마지막으로 더한 값은 sum 함수가 리턴한 채널에서 꺼내면 됩니다.

c := num(1, 2) // 1과 2가 들어있는 채널이 리턴됨
out := sum(c)  // 채널 c를 매개변수에 넘겨서 모두 더함, 더한 값이 들어있는 out 채널을 리턴

fmt.Println(<-out) // 3: out 채널에서 값을 꺼냄

저작권 안내

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

Published

01 June 2015