loading
본문 바로가기
Language/go lang.

[Go] Go언어 Channel

by 개발자 김모씨 2020. 9. 14.
반응형

(혹시 고루틴 포스팅을 보지 않으신 분은 먼저 훑고 오시길....)
artist-developer.tistory.com/14?category=951423

[Go] 고루틴(go-routine)

앞서 Go언어 입문에 포스팅하였듯, Go언어의 대표적 특징 중 하나에는 병행성이 있다. Go언어는 'go' 키워드를 사용하여, 고루틴을 만드는데, 여기서 고루틴은 비동기적(asynchronously)으로 특정 함수

artist-developer.tistory.com


우리는 이제 Go언어에서 병행성(Concurrency)을 어떻게 보장하는지 알게 되었다.
이처럼 병행성이 보장된 Multi-thread와 같은 환경에서
가장 중요한 것은 동기화(Synchronous) 이다.
Thread 간에 데이터를 공유하고 실행 순서를 제어해야 개발자가 원했던 결과물을 얻을 수 있다.
그렇지 않다면, Thread끼리 서로 다른 값을 읽어서 덮어쓴다거나 하는 오류가 발생할 수 있다.
그렇다면 Go언어에서는 고루틴 간에 데이터는 어떻게 주고 받을까?

채널

Go 언어에서는 채널(Channel)이라는 개념을 통해 고루틴간의 데이터 통신을 해결하였다.
채널이란, 동시에 실행되는 고루틴들을 연결해주는 파이프(pipe)라고 할 수 있다.

<가장 빨리 만나는 Go언어 발췌>

이처럼, 채널은 고루틴 사이에 위치하게 되며, 기본적으로 양방향성을 가진다.
모든 타입을 채널로 사용할 수 있으며, 채널 자체는 값이 아닌 레퍼런스 타입이다.

<가장 빨리 만나는 Go언어 발췌>

위 그림처럼, 채널은 반드시 두 개의 고루틴 사이에서만 존재할 수 있다. (다중 사용 X)

채널 사용법


그렇다면 채널은 어떻게 만들고 어떻게 쓸까?

//golang var channel1 chan int = make(chan int) var channel2 = make(chan int) channel3 := make(chan int)

채널은 "make(chan 자료형)"의 포맷으로 선언된다.
이 때 자료형이란, 채널에 들어가는 값의 자료형이다.
예를 들어 위처럼 int형 채널을 만들 경우, 해당 채널로는 int 자료형 변수만 주고받을 수 있다.

//golang func routine1(channel chan string) { // 채널에 값을 넣습니다. channel<- "data" } func routine2(channel chan string) { // 채널로부터 값을 꺼내서 출력합니다. fmt.Println(<-channel) // 채널에 값이 들어올때까지 대기 } func main() { // string 채널을 위한 메모리를 할당합니다. channel := make(chan string) go routine1(channel) go routine2(channel) } 
Output : data

채널을 사용하여 값을 주고받는 예제이다.
채널에 값을 넣고 뺄때는 '<-' 연산자가 사용된다.
- 넣을 때 : 채널변수<- 넣는 값 또는 변수
- 뺄 때 : 값을 넣을 변수 <- 채널변수, 값이 들어오면 가져옴. 값이 없을 시 대기
의 형태를 띈다.
고루틴으로 사용될 routine1, routine2 함수의 파라미터값으로 채널변수가 지정되었다.
그리고 main 함수에서 두 함수의 고루틴을 만들면서, 파라미터로 동일한 채널 변수를 넣어주었다.
이때 중요한 점이 한 가지 있는데,
채널에서 값을 뺄 때는 '채널에 값이 들어올 때까지 대기'한다. (매우 중요!)
10번째 Line rountine2의 코드에서
routine1에 의하여 채널변수 channel에 값이 들어올 때까지 해당 코드는 실행되지 않고 대기하고 있는다.
채널변수 channel에 값이 들어오면 해당 코드가 실행되고 고루틴이 종료된다.
고루틴 두 개(routine1, routine2)의 관계를 그림으로 표현하면 다음과 같다.

&lt;가장 빨리 만나는 Go언어 발췌&gt;


더 나아가,
양방향 파이프 형태의 채널이 아닌, 단방향 파이프 형태의 채널을 만들 수도 있다.

//golang func routine1(channel <- chan string) { // 채널에 값을 넣습니다. channel<- "data" } func routine2(<-channel chan string) { // 채널로부터 값을 꺼내서 출력합니다. fmt.Println(<-channel) // 채널에 값이 들어올때까지 대기 } func main() { // string 채널을 위한 메모리를 할당합니다. channel := make(chan string) go routine1(channel) go routine2(channel) } 

단방향 파이프를 위해서는,
함수의 선언부에 '<-' 연산자를 사용하여 어떤 용도로 사용할 채널인지 명시할 수 있다.


이처럼, Go 언어에서는 고루틴 간의 데이터 전송과 동기화를 위해 채널을 사용하고 있다.
Go 언어에서는 일반 언어들과 마찬가지로,
Race Condition(경쟁 조건) 상황에서의 동기화를 위한 기능을 추가로 제공하고 있는데,
이와 관련한 응용은 다음 포스팅에서 알아보도록 하겠다.

반응형

댓글