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 프로세스 능력 성숙도 모델(CMMI)의 적용 digipine 2017.10.28 698
103 프로그래밍 언어 순위 2023년 file digipine 2023.10.30 160
102 포렌식을 활용한 정보보호 digipine 2017.11.02 434
101 초고속망 통신사 DNS 서버 주소 모음 - DNS 설정 digipine 2017.11.03 2631
100 임베디드SW 개발자센터 이용안내(성남시 분당구, 개발공간 무료제공) digipine 2017.11.02 590
99 이벤트 텍소노미(Event Taxonomy)란 무엇인가요? digipine 2023.08.11 264
98 유닉스/리눅스 명령어 레퍼런스 digipine 2017.11.03 745
97 윈도우즈 도스 커멘드(Command) 네트워크 관련 명령어 lizard2019 2019.02.07 1342
96 윈도우 한영 전환 쉬프트 스페이스로 변경 digipine 2017.11.03 413
95 우분투 Nabi 한글 입력기 Tray(트레이) 상단 메뉴바로 옮기기 digipine 2017.11.03 1630
94 우분투 18.04 MongoDB 설치 및 구성 lizard2019 2021.02.26 503
93 언어 IDE 별로 git ignore 파일을 자동으로 만들어 주는 사이트 엉뚱도마뱀 2018.12.17 122650
92 악성코드 종류 구분 digipine 2017.11.13 962
91 수학적 구조물 모델링 만들기 소개 비디오 엉뚱도마뱀 2018.09.24 1051
90 소프트웨어 테스팅 전문가들을 위한 사이트 digipine 2017.11.02 610
89 비밀번호 해쉬에 Salt(소금) 첨가하기 file 엉뚱도마뱀 2017.11.23 4274
88 리눅스 커널의 Swap Memory에 대해서 digipine 2017.11.02 675
87 리눅스 /dev/random을 이용한 랜덤값 생성 엉뚱도마뱀 2017.11.22 1556
86 대칭키 암호화관련 개념 정리 digipine 2017.11.09 1642
85 난수발생기 개론 엉뚱도마뱀 2017.11.22 4319
Board Pagination Prev 1 2 3 4 5 6 Next
/ 6