반응형
Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- UNIX
- 함수포인터
- 긴옵션
- newSQL
- kernel
- bash
- FreeBSD
- 구조와 원리
- Programming
- 커널
- DBMS
- 포인터변수
- Golang
- DBMS 개발
- SQLite
- Windows via c/c++
- TiDB
- 전처리기
- getopts
- 포인터
- Symbol
- OS 커널
- 약어
- Preprocessor
- 한빛미디어
- 컴퓨터 강좌
- TiKV
- go
- Pointer
- UNIX Internals
Archives
- Today
- Total
sonumb
Go - 채널 잘 이용해보기 본문
개요 및 문제 상황.
Go언어에서 동기화 툴로 제공하는 것중 하나가 채널이다.
다만, 채널의 특성으로 인해, 사용할 때 문제 혹은 불편한 사실들이 있다.
- 다른 누군가가 쓰기 전까지, 읽는 것은 반환되지 않는다.(단, 채널 용량에 따라 반환여부가 결정 된다)
- 다른 누군가가 읽기 전까지, 쓰는 것은 반환되지 않는다.
close(ch)
을 하면, 위 두가지 문제(반환되지 않음)이 해결되는가 싶어서, 이를 시도한다.- 닫힌 채널에 쓰는 행위는 panic이 발생한다
- 그런데, 닫힌 채널을 읽는 행위는 잘 실행된다(!)
해결방법
3-1번 상황을 해결하기 위해, 채널을 구성할 때는 full-duplex로 구성한다. 즉 쓰기/읽기를 전용 채널을 각각 선언하고, 닫히는 시점을 잘 정의해야 한다.(대개 다수의 생산자 쓰레드가 모두 종료된 후, 상위 함수에서 채널을 닫는 쪽으로 설계/구현한다)
3-2번 상황을 해결하기 위해, 두 가지 방법으로 구현할 수 있다.
- 채널을 닫고, ch = nil을 세팅한다. 채널에 액세스 할 때, ch == nil 을 검사하여 채널을 읽기/쓰기를 한다.
- 채널을 읽을 때 닫힘 상태도 같이 반환 받는다
위 2번 같은 경우에 for range
를 이용하면 간단하게 구현할 수 있다. 아래 소스코드의 channelData1()
함수에 해당한다.
그러나 다수의 채널에 대한 읽기를 처리하기 위해 select
를 써야 하는 경우에는 for range
를 사용하지 못한다.
이런 경우, data , isOpened := <-ch
와 같이 데이터와 닫힘 여부를 동시해 받아서 처리할 수 있다. 이 경우는 아래 소스코드의 channelData2()
에 해당한다.
소스코드
package main
import (
"fmt"
"time"
"runtime"
)
func trace() (string, int, string) {
pc, file, line, ok := runtime.Caller(1)
if !ok { return "?", 0, "?" }
fn := runtime.FuncForPC(pc)
if fn == nil { return file, line, "?" }
return file, line, fn.Name()
}
func channelData1() {
ch := make(chan int)
go func() {
for i := 0 ; i < 5 ; i++ {
ch <- i
time.Sleep(time.Second)
}
close(ch)
}()
for data := range ch {
fmt.Println(data)
}
fmt.Println("channelData1() : done")
}
func channelData2() {
ch := make(chan int)
go func() {
for i := 0 ; i < 5 ; i++ {
ch <- i
time.Sleep(time.Second)
}
close(ch)
}()
for stopLoop := false; stopLoop != true ; {
select {
case data, isOpened := <-ch:
if isOpened == true {
fmt.Println(data)
} else {
stopLoop = true
}
}
}
fmt.Println("channelData2() : done")
}
func main() {
channelData1()
channelData2()
}
결과
$ go build ex.go; ./ex
0
1
2
3
4
channelData1() : done
0
1
2
3
4
channelData2() : done
$
반응형