sonumb

Go - 채널 잘 이용해보기 본문

개발자 이야기/Go

Go - 채널 잘 이용해보기

sonumb 2021. 11. 4. 13:02

개요 및 문제 상황.

Go언어에서 동기화 툴로 제공하는 것중 하나가 채널이다.

 

다만, 채널의 특성으로 인해, 사용할 때 문제 혹은 불편한 사실들이 있다.

  1. 다른 누군가가 쓰기 전까지, 읽는 것은 반환되지 않는다.(단, 채널 용량에 따라 반환여부가 결정 된다)
  2. 다른 누군가가 읽기 전까지, 쓰는 것은 반환되지 않는다.
  3. close(ch)을 하면, 위 두가지 문제(반환되지 않음)이 해결되는가 싶어서, 이를 시도한다.
    1. 닫힌 채널에 쓰는 행위는 panic이 발생한다
    2. 그런데, 닫힌 채널을 읽는 행위는 잘 실행된다(!)

해결방법

3-1번 상황을 해결하기 위해, 채널을 구성할 때는 full-duplex로 구성한다. 즉 쓰기/읽기를 전용 채널을 각각 선언하고, 닫히는 시점을 잘 정의해야 한다.(대개 다수의 생산자 쓰레드가 모두 종료된 후, 상위 함수에서 채널을 닫는 쪽으로 설계/구현한다)

 

3-2번 상황을 해결하기 위해, 두 가지 방법으로 구현할 수 있다.

  1. 채널을 닫고, ch = nil을 세팅한다. 채널에 액세스 할 때, ch == nil 을 검사하여 채널을 읽기/쓰기를 한다.
  2. 채널을 읽을 때 닫힘 상태도 같이 반환 받는다

위 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

$
반응형