19.1. 프로그램언어 고(Go)의 채널 생성 및 활용

프로그램언어 고(Go)에서의 unbuffered channel 생성 및 사용

고(Go)언어에서 unbuffered 채널은 버퍼가 없는 채널을 말합니다. 이는 make 함수를 사용하여 생성할 수 있습니다.


ch := make(chan int) // unbuffered 채널

위 코드처럼 make시에 버퍼 크기를 명시하지 않으면 unbuffered 채널이 생성됩니다.

unbuffered 채널의 특징은 데이터를 주고받을 때 반드시 서로를 기다려야 한다는 것입니다. 예를 들어 ch 채널로 값을 보내기 위해서는 반드시 반대편에서 값을 받아가기 전까지 기다려야 합니다.


func main() {
  ch := make(chan int) // unbuffered
  
  go func() {
    val := <-ch
    fmt.Println(val)
  }()
  
  ch <- 3
}

위 예제를 보면 main 고루틴이 ch 채널로 3을 보내기 위해서는 반대편 고루틴이 <-ch를 통해 그 값을 받아가기 전까지 기다려야 합니다. 이와 반대로 값을 받기 위해서도 마찬가지입니다.


func main() {
  ch := make(chan int) // unbuffered

  go func() {
    ch <- 3
  }()

  val := <-ch 
  fmt.Println(val)  
}

위와 같이 val := <-ch에서 값을 기다리기 위해서는 반대편 고루틴이 ch <- 3을 통해 값을 보낼 때까지 기다려야 합니다. 이처럼 unbuffered 채널은 동기화를 보장하기 위해 서로를 기다리므로 데이터 교환 시 타이밍이 맞춰져야 하는 경우에 유용합니다. 다만 데이터 전송마다 기다리는 오버헤드가 있어 성능 상 부담이 될 수 있습니다. 이 정도로 unbuffered 채널의 생성과 사용 방법에 대해 간략히 설명드렸습니다. 더 자세한 내용이 필요하시다면 댓글로 알려주시기 바랍니다.

프로그램언어 고(Go)에서의 buffered channel 생성 및 사용

Go언어에서 버퍼드 채널은 크기가 있는 채널로, 만들 때 버퍼 크기를 지정할 수 있습니다. 이 버퍼는 보내기 전에 데이터를 임시로 저장할 수 있는 공간입니다.

버퍼드 채널을 만드는 방법은 make 함수를 사용하여 다음과 같이 작성합니다.


ch := make(chan int, 100)

위의 코드는 크기가 100인 int형 버퍼드 채널을 만듭니다.

버퍼드 채널의 장점은 보내기(send) 연산이 받기(receive) 연산과 동기화되지 않아도 된다는 점입니다. 즉, 보내기 함수를 호출할 때 수신할 준비가 되어 있지 않더라도 버퍼에 데이터를 저장할 수 있습니다. 수신하는 측이 준비된 시점에 버퍼에서 데이터를 가져갈 수 있습니다.

버퍼드 채널 사용 예시는 다음과 같습니다.


package main

import "fmt"

func main() {

    // 버퍼 크기가 2인 채널
    ch := make(chan int, 2)

    ch <- 1 // 버퍼에 데이터 1 저장
    ch <- 2 // 버퍼에 데이터 2 저장

    fmt.Println(<-ch) // ch에서 데이터 1 출력
    fmt.Println(<-ch) // ch에서 데이터 2 출력
}

위의 코드에서 크기가 2인 버퍼드 채널을 만들고, 보내기 연산을 통해 버퍼에 데이터를 저장합니다. 그리고 나중에 받기 연산을 통해 버퍼된 데이터를 가져옵니다.

버퍼가 가득 차면 보내기 연산이 블로킹됩니다. 즉 수신자가 데이터를 가져가서 버퍼 공간이 생기기 전까지 기다려야 합니다.

이와 같이 Go언어의 버퍼드 채널은 송수신 간의 동기화 문제를 완화하여 병렬 처리 성능을 높일 수 있습니다.

프로그램언어 고(Go)에서의 채널을 이용한 고루틴 동기화

Go 언어의 고루틴과 채널을 이용하면 코드 실행 흐름을 동기화할 수 있습니다.

고루틴은 Go 언어의 가볍고 효율적인 스레드입니다. 여러 고루틴이 동시에 실행될 수 있습니다.

채널은 고루틴 간에 데이터를 주고받는 통로 역할을 합니다. 채널을 통해 한 고루틴이 다른 고루틴으로부터 데이터를 받거나, 다른 고루틴에게 데이터를 보낼 수 있습니다.


package main

import "fmt"

func sum(s []int, c chan int) {
    sum := 0
    for _, v := range s {
        sum += v
    }
    c <- sum // sum을 채널 c로 보냄
}

func main() {
    s := []int{7, 2, 8, -9, 4, 0}

    c := make(chan int)
    go sum(s[:len(s)/2], c)
    go sum(s[len(s)/2:], c)
    x, y := <-c, <-c // 채널 c로부터 두 개의 값을 받음

    fmt.Println(x, y, x+y)
}

위 예제에서 main 고루틴이 실행되고 나면 배열을 반으로 나누어 두 개의 고루틴이 만들어집니다.

각 고루틴은 주어진 배열 요소의 합을 구합니다.

고루틴 내부에서 계산한 합은 채널 c를 통해 보냅니다.

main 고루틴은 채널 c로부터 두 개의 합 결과를 받아서 출력합니다.

채널을 이용하면 동기화가 보장됩니다.

main 고루틴은 채널 c로부터 값을 받을 때까지 기다리다가, 고루틴들이 값을 보내면 해당 값을 받고 처리를 계속 합니다.

이로써 복잡한 lock이나 condition 없이 고루틴간 통신과 동기화를 쉽게 구현할 수 있습니다.

프로그램언어 고(Go)에서의 채널로 메시지 전달하기

Go 언어에서 채널은 고루틴 간에 데이터와 신호를 안전하게 전달하는 통신 수단을 제공합니다.


package main

import "fmt"

func main() {

  // 채널 만들기
  ch := make(chan int)

  // 고루틴에서 채널에 데이터 보내기
  go func() {
    ch <- 42
  }()

  // 채널에서 데이터 받기
  fmt.Println(<-ch)
}

위의 예제에서,

1. make를 사용하여 int 타입의 값을 전달할 수 있는 채널 ch를 만듭니다.

2. 별도의 고루틴에서 ch 채널을 통해 42라는 정수를 보냅니다. <- 연산자를 사용하여 데이터를 채널에 보냅니다. 3. 메인 고루틴에서는 <-ch 표현식을 사용하여 채널로부터 데이터를 받습니다. 이러한 송신(send)과 수신(receive)을 통해 고루틴들 간에 데이터를 안전하게 교환할 수 있습니다. 채널을 사용할 때 주의할 점은, - 송신과 수신을 위해서는 반드시 채널을 만들어야 합니다. - 수신보다 많은 송신 시도 시 프로그램이 블록될 수 있습니다. - 채널을 닫는 close 함수를 사용하여 더 이상 사용하지 않는 채널을 닫아줘야 합니다. 이 외에도 채널의 버퍼 크기 조정, select 문을 사용한 복수 채널 동시 사용 등 고급 사용법이 있습니다. 채널은 Go의 가장 큰 특징 중 하나이며, 병행성 프로그래밍을 가능하게 해주는 매우 유용한 특성입니다. 위 예제와 같이 사용법만 알면 쉽고 효과적으로 고루틴간 통신이 가능합니다. 본문을 참고하시어 Go 언어의 채널에 대한 이해를 넓히시기 바랍니다.

프로그램언어 고(Go)에서의 채널에서 값을 받아오기

Go 언어의 채널을 사용하여 값을 받아오는 방법은 다음과 같습니다.


package main

import "fmt"

func main() {

    // 채널 만들기
    ch := make(chan int)
    
    // goroutine에서 채널로 값 보내기
    go func() {
        ch <- 55 
    }()

    // 채널로부터 값 받아오기
    num := <-ch
    
    fmt.Println(num) // 55
}

위의 예제코드에서 보시다시피,
main 함수에서 우선 채널을 만들었습니다.

ch := make(chan int)

여기서 ch는 채널의 변수명이고, chan int는 정수형 값을 보내고 받을 수 있는 채널을 정의한 것입니다.

그리고 바로 goroutine을 실행시켰는데, 여기서는 익명 함수를 사용하였습니다.

go func() {
ch <- 55 }()

goroutine 내에서는 채널 ch에 55라는 값을 보내고 있습니다. <- 연산자를 사용하여 값을 채널로 보냅니다. 마지막으로 main 함수에서는 num := <-ch

코드를 사용하여 채널 chから 값을 받아오고 있습니다.

<- 연산자를 사용하여 채널로부터 값을 받아옵니다. 여기서 받아온 값을 num 변수에 할당하고 있습니다. 이러한 방식으로 Go 언어의 채널을 사용하여 goroutine과 main 함수之间에 값을 전달할 수 있습니다. 채널은 Go의 동시성 프로그래밍을 위한 가장 중요한 기능 중 하나이므로 꼭 숙지하시기 바랍니다.

Leave a Comment