7.3. 프로그램언어 고(Go)의 select문

프로그램언어 고(Go)의 select문의 기본 개념

고(Go)언어의 select문에 대해 설명드리겠습니다.

select문은 고루틴에서 사용하는 채널(channel)의 통신을 위한 문법입니다. 복수의 채널에서 값을 입력받거나 보내고자 할 때 사용합니다.

예를 들어 다음과 같이 c1과 c2 두 개의 채널이 있다고 합시다.

c1 := make(chan int)
c2 := make(chan int)

이 두 채널에 값을 보내거나 받고 싶다면 select문을 이용할 수 있습니다.

select {
  case <-c1:
    fmt.Println("c1 채널에서 값을 받음")
  case <-c2:  
    fmt.Println("c2 채널에서 값을 받음")  
}

위 코드에서 보시다시피 case문을 이용하여 각 채널과의 통신 CASE를 정의합니다.

그리고 select문 실행시 먼저 값을 보내는 쪽 채널의 case문부터 실행됩니다. 만약 c1채널에 먼저 값이 보내지면 c1 case문이 수행되고, c2채널에 먼저 값이 보내지면 c2 case문이 수행됩니다.

즉, 복수의 채널간의 동기화를 쉽게 할 수 있는 문법이라고 보시면 됩니다.

다음은 time.After 함수를 사용하여 5초 타임아웃도 함께 정의한 예제입니다.

select {
  case <-c1:
    fmt.Println("c1 채널에서 값을 받음") 
  case <-c2: 
    fmt.Println("c2 채널에서 값을 받음")
  case <-time.After(5 * time.Second):
    fmt.Println("5초 타임아웃 발생")  
}

위 case문 중 먼저 수행 가능한 것이 실행되고, 5초 안에 c1과 c2 채널 모두 값을 받지 못하면 타임아웃 case문이 수행됩니다.

이 외에도 select문에 default case를 추가할 수 있습니다.

select {
case communication:
    // channel communication
default:
    // no channels are ready  
}

default case는 어떤 채널 통신도 발생하지 않을 때 실행됩니다.

이상으로 고(Go)언어의 select문 사용법에 대해 간략히 설명드렸습니다. 복수의 고루틴과 채널간 통신 동기화에 유용하게 사용되므로 꼭 숙지하시기 바랍니다.

프로그램언어 고(Go)에서의 select문의 사용 방법

고(Go)언어의 select문 사용방법을 설명드리겠습니다.

select문은 고루틴에서 동시에 여러 채널의 통신을 기다리는 기능을 제공합니다. 채널로부터 데이터가 도착할 때까지 기다리다가, 데이터가 도착한 채널이 실행을 계속하도록 해주죠.

예를 들어 2개의 채널이 있다고 가정했을 때,


ch1 := make(chan int)
ch2 := make(chan int)

select {
case <-ch1:
    fmt.Println("Channel 1 has data")
case <-ch2: 
    fmt.Println("Channel 2 has data")
}

위와 같이 select문을 사용하면 ch1채널과 ch2채널에서 데이터가 올 때까지 기다립니다. 그리고 데이터가 도착한 채널이 실행을 계속합니다.

즉, ch1채널에 데이터가 먼저 도착했다면 "Channel 1 has data"가 출력되고, ch2채널에 먼저 데이터가 도착했다면 "Channel 2 has data"가 출력되는 방식입니다.

select문에 default 문을 추가할 수도 있습니다.


select {
case <-ch1:
    fmt.Println("Channel 1 has data") 
case <-ch2:
    fmt.Println("Channel 2 has data")
default:
    fmt.Println("No data received")  
}

위 코드에서 보면 default문은 채널 모두로부터 데이터가 도착하지 않았을 때 실행됩니다. 즉, 타임아웃 기능을 제공한다고 보시면 됩니다.

또한 select문에는 위에서 설명한 case문 외에도 다양한 문을 사용할 수 있습니다.


select {
case i := <-ch1:
    fmt.Printf("received %v from ch1", i)
case ch2 <- 3:
    fmt.Println("sent 3 to ch2") 
default:
    fmt.Println("no operations")
}

위 예제코드에서 보다시피 send/receive 문을 사용할 수도 있고, 변수 할당 문도 사용할 수 있습니다. 매우 유연합니다.

이외에도 select문은 고루틴 사이의 타임아웃이나 취소 등을 쉽게 구현할 수 있고 동기화 프리미티브로도 사용할 수 있기 때문에 고루틴과 채널을 사용할 때 꼭 알아둬야 하는 문법이라고 볼 수 있습니다.

지금까지 고(Go)언어의 select문 사용방법에 대해 간략히 설명해드렸습니다. 보다 자세한 사항은 공식문서를 참고해주시기 바랍니다.

프로그램언어 고(Go)의 select문에서의 복수의 채널 선택

Go 언어의 select문은 복수의 채널에서 데이터를 읽거나 쓰기를 기다리는 동시성 프리미티브입니다. select문을 사용하면 한 번의 호출로 여러 채널의 통신을 동시에 처리할 수 있습니다.

예를 들어 다음과 같이 2개의 채널 ch1, ch2가 있다고 합시다.

ch1 := make(chan int)
ch2 := make(chan string)

여기에서 ch1 채널과 ch2 채널 모두 데이터를 보내거나 받을 준비가 되어 있다고 할 때, select문을 사용하면 두 채널에서 동시에 통신을 기다릴 수 있습니다.

select {
case i := <-ch1:
    fmt.Println(i)
case s := <-ch2:
    fmt.Println(s)  
}

위의 코드는 ch1 채널과 ch2 채널 둘 다로부터 데이터를 읽기 위해 대기합니다. 만약 ch1에서 정수값이 먼저 도착한다면 i 변수에 할당되고, ch2에서 문자열이 먼저 도착한다면 s 변수에 할당됩니다.

즉, select문을 사용하면 복수의 채널 통신을 동시에 기다리다가 가장 먼저 데이터 통신이 이뤄지는 채널을 우선적으로 처리할 수 있습니다. 이를 통해 채널들 간의 동시성 통신을 쉽고 효과적으로 다룰 수 있습니다.

select문에 case문을 추가하여 복수의 채널 통신 조건을 한번에 지정할 수 있습니다. 아래와 같이 작성할 수 있습니다.

func process(ch1, ch2 chan int) {
    select { 
    case <-ch1:
        // ch1에서 데이터를 읽은 후 처리
    case <-ch2:
	    // ch2에서 데이터를 읽은 후 처리
    case <-time.After(time.Second * 1):
        // 1초 안에 ch1, ch2 모두 데이터를 못 읽으면 처리
    }
}  

위 예제에서 ch1, ch2 채널 뿐 아니라 time.After()를 사용하여 타임아웃도 함께 지정했습니다.

이를 통해 일정 시간 이내에 채널 통신이 이뤄지지 않으면 타임아웃 조건문을 실행할 수 있습니다. 복수의 채널과 타임아웃을 모두 고려한 통신 코드를 작성할 수 있습니다.

이 외에도 Go select문은 채널의 닫힘 상태를 체크하는 기능, default 문을 사용한 추가 처리 기능 등 다양한 방법으로 활용이 가능합니다.

위 예제와 설명에서 Go언어의 select문을 사용한 복수 채널 동시 통신 기능에 대해 간단히 살펴보았습니다. select문은 Go의 동시성 프로그래밍에 있어 매우 중요한 기능이므로 꼭 숙지하시기 바랍니다.

프로그램언어 고(Go)에서의 select문과 default case의 사용

Go언어의 select문은 channel이나 다른 타입의 값들을 대기하다가 ready 상태가 되면 해당 case문을 실행시켜주는 제어문입니다.


package main

import "fmt"

func main() {

    c1 := make(chan string)
    c2 := make(chan string)

    go func() {
        time.Sleep(1 * time.Second)
        c1 <- "one"
    }()

    go func() {
        time.Sleep(2 * time.Second) 
        c2 <- "two"
    }()

    for i := 0; i < 2; i++ {
        select {
        case msg1 := <-c1:
            fmt.Println("received", msg1)
        case msg2 := <-c2:
            fmt.Println("received", msg2)
        }
    }
}

위 예제코드에서 보다시피, c1과 c2 채널을 select문 안에서 대기하고 있습니다.

c1채널은 1초 뒤에 "one" 문자열을 보내고, c2채널은 2초 뒤에 "two" 문자열을 보냅니다.

select문은 이 채널들 중 어느 것이든 데이터를 보내기 시작하면(ready 상태가 되면) 해당 case문을 실행합니다.

위 예제의 출력은 아래와 같습니다:

```
received one
received two
```

즉, c1채널이 먼저 ready 상태가 되면 그 쪽 case문이 먼저 실행되고, 다음턴에는 c2채널이 ready상태가 되면서 그 쪽 case문이 실행됩니다.

select문에 default case문을 사용할 수도 있습니다.

default case문은 아무런 채널도 ready 상태가 아니면 실행됩니다.


package main

import (  
    "fmt"
    "time"
)

func main() {

    c1 := make(chan string)
    c2 := make(chan string)

    go func() {
        time.Sleep(2 * time.Second)
        c1 <- "result 1"
    }()

    go func() {
        time.Sleep(3 * time.Second) 
        c2 <- "result 2"
    }()

    ticker := time.NewTicker(1 * time.Second)
    defer ticker.Stop()

loop:
    for {
        select {
        case r := <-c1:
            fmt.Println("received", r)
            break loop
        case r := <-c2:
             fmt.Println("received", r)
             break loop
        case <- ticker.C:
            fmt.Println("no result yet...")
        default:
            fmt.Println("waiting...")
            time.Sleep(400 * time.Millisecond)
        }
    }

    fmt.Println("end.")
}

위 예제에서 default case문은 400밀리초마다 "waiting..."을 출력합니다.

c1, c2 채널이 모두 ready 상태가 아니면 default case문이 실행된다는 것을 확인할 수 있습니다.

이상 Go언어의 select문과 default case문에 대한 설명이었습니다. 도움이 되었길 바랍니다.

프로그램언어 고(Go)에서의 select문에서의 채널 우선순위 설정

Go언어의 select문은 채널의 통신을 위해 사용되며, 여러 채널들 중에서 통신이 가능한 채널을 선택하는 데 사용됩니다.

select문 내부에서 채널의 우선순위를 정하는 방법은 다음과 같습니다.


import "time"
import "fmt"

func main() {

  c1 := make(chan string)
  c2 := make(chan string)

  go func() {
    time.Sleep(1 * time.Second)
    c1 <- "one"
  }()

  go func() {
    time.Sleep(2 * time.Second) 
    c2 <- "two"
  }()

  for i := 0; i < 2; i++ {
    select {
    case msg1 := <-c1:
      fmt.Println("received", msg1)
    case msg2 := <-c2:
      fmt.Println("received", msg2)
    }
  }
}

위 코드에서 c1 채널은 1초 뒤에, c2 채널은 2초 뒤에 값을 보냅니다.

Select문은 가장 먼저 통신이 가능한 채널을 선택합니다.

첫 번째 반복에서는 c1 채널이 1초가 지나 값을 보내기 때문에 c1이 선택됩니다.

두 번째 반복에서는 c1과 c2 모두 값을 보냈지만, c1이 더 빨리 값을 보냈기 때문에 c2가 선택됩니다.

즉, Go의 select문 내부에서 채널의 우선순위는 통신 가능 시점의 빠른 순으로 결정됩니다.

가장 먼저 통신할 수 있는 채널이 가장 높은 우선순위를 가집니다.

위 예제코드에 주석을 추가하여 좀 더 자세히 설명하겠습니다.


import "time" 
import "fmt"

func main() {

  c1 := make(chan string) // c1 채널 생성
  c2 := make(chan string) // c2 채널 생성

  go func() {
    time.Sleep(1 * time.Second) 
    c1 <- "one" // 1초 뒤 c1 채널로 "one" 문자열 전송
  }()

  go func() {
    time.Sleep(2 * time.Second)
    c2 <- "two" // 2초 뒤 c2 채널로 "two" 문자열 전송
  }()

  for i := 0; i < 2; i++ {
    select {
    case msg1 := <-c1: // c1 채널 수신
      fmt.Println("received", msg1) 
    case msg2 := <-c2: // c2 채널 수신  
      fmt.Println("received", msg2)
    }
  }
}

위 코드에서 볼 수 있듯이, c1과 c2 채널은 동시에 값을 전송합니다.

하지만 전송 시점이 다르기 때문에 select문 내에서의 우선순위가 다릅니다.

가장 먼저 통신 가능한 채널이 우선순위도 가장 높다고 볼 수 있습니다.

이상으로 Go언어의 select문과 채널 우선순위 설정에 대한 설명을 마치겠습니다.

공부에 도움이 되었기를 바랍니다.

Leave a Comment