logo

English

이곳의 프로그래밍관련 정보와 소스는 마음대로 활용하셔도 좋습니다. 다만 쓰시기 전에 통보 정도는 해주시는 것이 예의 일것 같습니다. 질문이나 오류 수정은 siseong@gmail.com 으로 주세요. 감사합니다.

Golang Channel 사용법 정리

by digipine posted Oct 22, 2021
?

Shortcut

PrevPrev Article

NextNext Article

Larger Font Smaller Font Up Down Go comment Print
?

Shortcut

PrevPrev Article

NextNext Article

Larger Font Smaller Font Up Down Go comment Print

 

channel에 send할 때는 receive할 준비가 되어 있어야 한다.

c := make(chan string)
c <- "Hello"
<-c // 오류 발생: fatal error: all goroutines are asleep - deadlock!

위와 같은 경우, 코드가 순차적으로 실행된다.
2번째 라인이 실행된 후에 3번째 라인이 실행되는데,
2번째 라인에서 send하려는 시점에 3번째 라인에서 receive할 준비가 되지 않아서 보낼 수가 없다.


c := make(chan string, 1)
c <- "Hello"
fmt.Println(<-c) // 출력: Hello

버퍼가 있는 채널이라면 정상적으로 동작할 수 있다.
send하려는 시점에 receive할 준비는 되어있지 않지만, 버퍼에 넣어두면 되니까 괜찮다.


c := make(chan string)
go func() {
	c <- "Hello"
}()
fmt.Println(<-c) // 출력: Hello

정상적으로 실행된다. 왜 가능할까?
고루틴은 스케줄링되어 실행되기 때문이다.

실행 순서
1. 고루틴이 스케줄링 된다.
2. <-c에 왔지만 send된 데이터가 없어서 대기
3. 어느 시점에 고루틴이 실행된다. c <- "Hello"
4. fmt.Println(<-c)


range는 channel이 close되어야 끝난다.

channel에서 데이터를 receive 받는 방법으로 range가 있다. 이때, range는 channel이 close되어야 끝난다. 고로, channel을 close해주지 않는다면 영원히 기다리면서 deadlock이 발생한다.

ch := make(chan int, 1)
ch <- 101
for value := range ch {
	fmt.Println(value)
}
Output:
101
fatal error: all goroutines are asleep - deadlock!

101을 보낸 것은 잘 받아서 처리했지만, 그 후에 close되지 않아서 deadlock이 발생한다.


close된 channel에 send 할 수 없다.

ch := make(chan int)
close(ch)
ch <- 1 // panic: send on closed channel

close된 channel에서 receive 할 수 있다.

ch := make(chan int, 2)

var wg sync.WaitGroup
wg.Add(1)
go func() {
	ch <- 10
	ch <- 11
	wg.Done()
}()

wg.Wait()
close(ch)
fmt.Println(<-ch) // 10
fmt.Println(<-ch) // 11
fmt.Println(<-ch) // 0

waitGroup에 대해 잘 모르는 분이라면...
버퍼가 2개인 채널에 10, 11을 send할 때까지 기다린 후에,
channel을 close하고 receive하는 코드라고 이해하자.

close된 channel에 send 할 수는 없지만 receive할 수는 있다.
버퍼에 저장된 값은 고대로 읽어오고 값이 없다면 zero value를 가져온다.


nil 채널에 send/receive하면 영구 대기


select

select문은 무한루프가 아니지만, case가 올 때까지 기다린다.

select는 무한루프가 아니기 때문에 for-select 형식으로 사용된다.
하지만, case가 발생할 때까지 대기한다.

ch := make(chan int)
takeSomeTime := func()  {
	go func() {
		time.Sleep(time.Second * 2)
		ch <- 1
	}()
}

start := time.Now()
takeSomeTime()
		
select {
case <-ch:
	fmt.Println(time.Since(start)) // 2.004948752s
}

takeSometime은 2초간 대기한 후에 채널로 데이터를 보내는 함수이다. 요 데이터를 보내야 case는 값을 receive할 수 있다. 만약 select문에서 <-ch 발생을 기다리지 않고 넘어간다면 Output이 출력되지 않았을 것이다. 하지만 <-ch이 발생할 때까지 기다렸기 때문에 대략 2초 후에 출력되었다.

default가 있다면 고놈을 실행한다.

select에서 case는 순차적으로 실행되지 않는다.

select에서 case는 무작위로 실행되며, 각 case는 모두 비슷한 확률로 실행된다. (균일한 의사 무작위 선택)

c1 := make(chan interface{})
close(c1)
c2 := make(chan interface{})
close(c2)

var c1Count, c2Count int
for i := 1000; i >= 0; i-- {
	select {
	case <-c1:
		c1Count++
	case <-c2:
		c2Count++
	}
}

fmt.Printf("c1Count: %d\nc2Count: %d\n", c1Count, c2Count)
Output
c1Count: 519
c2Count: 482

대략 비슷하게 case가 실행된다.
(아까 위에서 언급했듯이, close해도 receive할 수 있기 때문에 위의 예는 문제가 없이 동작한다.)

case에 함수가 있다면 그 함수가 끝난 후, 다른 case를 검사한다.

select {
case <-ch:
case ch <- doSomething:
}

channel로 값이 오는 것을 기다리는 것 외에도 channel에 값을 전달하는 식으로도 select문을 이용할 수 있다. 이때, doSoemthing이 끝날 때까지 다른 case는 검사하지 않는다.

longFunction := func() interface{} {
	defer fmt.Println("end long function")
	fmt.Println("start long function")
	time.Sleep(time.Second)
	return nil
}

shortFunction := func() interface{} {
	defer fmt.Println("end short function")
	fmt.Println("start short function")
	return nil
}

chan1 := make(chan interface{},10)
chan2 := make(chan interface{},10)

for i := 0; i < 10; i++ {
	select {
	case chan1 <- longFunction():
	case chan2 <- shortFunction():
	}
}
start long function
end long function
start short function
end short function
start long function
end long function
start short function
end short function
start long function
...

소요시간이 긴 long function이 끝난 후에 다른 case의 short function이 실행되는 것을 볼 수 있다. select case에서 값을 send하는 경우에는 말 그대로 해당 case line이 실행된다고 이해하면 되겠다.


case에 있는 함수는 끝까지 실행하지만, 적절하지 않은 상황이라면 다른 case를 실행한다.

select {
case chan1 <- funcA():
case chan2 <- funcB():
}

위와 같은 상황에서 funcA를 먼저 처리한다고 가정하자. 고러면 funcA 함수는 끝까지 실행된다. 그런데 끝까지 실행하고 보니 chan1이 데이터를 받지 못하는 상황(버퍼가 꽉찬 채널)일 수 있다. 그러면 funcB()로 차례가 넘어간다.

chan1 := make(chan interface{})
chan2 := make(chan interface{},10)

functionA := func() interface{} {
	defer fmt.Println("end a function")
	fmt.Println("start a function")
	return nil
}

functionB := func() interface{} {
	defer fmt.Println("end b function")
	fmt.Println("start b function")
	return nil
}

for i := 0; i < 10; i++ {
	select {
	case chan1 <- functionA():
		fmt.Println("case A running")
	case chan2 <- functionB():
		fmt.Println("case B running")
	}
}

위 예제를 보면 channel에 데이터를 보내기는 하지만 받는 부분이 없다. ch1의 경우는 버퍼가 없기 때문에 데이터를 보낼 수 없고, ch2의 경우에는 버퍼가 넉넉하게 있기 때문에 데이터를 보낼 수 있다. 위 코드를 실행하면 다음과 같은 결과가 나온다.

start a function
end a function

start b function
end b function

case B running

start a function
end a function

start b function
end b function

case B running

case문에 있는 funtionA(), functionB() 모두 실행되지만 실질적으로 채널에 데이터를 보낸 것은 functionB()만 가능하다.

TAG •

List of Articles
No. Subject Author Date Views
104 TCP/IP State Transition - TCP 스택 포팅 시 참조 file digipine 2017.11.02 194832
103 언어 IDE 별로 git ignore 파일을 자동으로 만들어 주는 사이트 엉뚱도마뱀 2018.12.17 122391
102 What is Android Repo? and Manual, Download file digipine 2017.11.02 99553
101 [Linux] ubuntu 16.04에 QT Creator 설치하기 digipine 2017.11.02 24331
100 [Swift, MacOS] 맥 한글 파일명이 윈도우에서 자소 분리되는 현상 해결, NFD, NFC 엉뚱도마뱀 2018.12.11 20072
99 Linux init.d 에서 등록하기. 부팅 시 자동실행 설정 digipine 2017.11.03 12565
98 FFT (Fast Fourier Transform) 고속 푸리에 변환 예제 소스 digipine 2017.10.29 12544
97 Photoshop CC 2018 한글 영문 변환 언어팩, 포토샵 언어변경 file 엉뚱도마뱀 2018.07.04 8525
96 Phabricator 설치 가이드 우분투 16.04 기준 digipine 2017.11.02 6193
95 ATmega8 MCU 간의 TWI 기능을 이용한 I2C 통신 digipine 2017.11.02 5986
94 WinPCap과 Ethereal, Wireshark 을 이용한 스니핑(Sniffing) digipine 2017.10.29 5428
93 공짜 무료 C/C++ 컴파일러들 file digipine 2017.10.28 4376
92 난수발생기 개론 엉뚱도마뱀 2017.11.22 4311
91 비밀번호 해쉬에 Salt(소금) 첨가하기 file 엉뚱도마뱀 2017.11.23 4272
90 [iOS] Bluetooth로 App을 백그라운드 모드로 실행는 방법 lizard2019 2020.02.11 3514
89 OpenAL 사용법 정리 1 digipine 2017.11.01 3397
88 [Linux, OSX] pfctl - Packet FIlter Control 사용법 digipine 2017.11.02 3179
87 Xcode 없이 맥에 '명령어 라인 도구(Command Line Tools)'를 설치하는 방법 엉뚱도마뱀 2018.12.26 2859
86 WIN CE, GPS - NMEA protocol - GPS Virtual Driver digipine 2017.10.28 2829
85 초고속망 통신사 DNS 서버 주소 모음 - DNS 설정 digipine 2017.11.03 2630
Board Pagination Prev 1 2 3 4 5 6 Next
/ 6