Go Channel❓

beluga000·2024년 8월 1일
post-thumbnail

Go Channel

Golang에서 채널(Channel)은 고루틴(Go Routine)간의 통신과 동기화를 위한 도구입니다. 쉽게 말하면 데이터가 이동하는 통로라고 할 수 있습니다.

채널 생성

채널은 make 함수를 사용하여 생성합니다. 채널의 타입은 chan T로, T는 채널을 통해 전송될 값의 타입에 해당합니다.

ch := make(chan int) // int 타입 값을 전송할 수 있는 채널 생성

채널 값(데이터) 보내기

채널에 값(데이터)을 보낼 때는 <- 연산자를 사용합니다. 이 연산자는 값이 수신될 때까지 고루틴을 블록(block)합니다.

ch <- 42 // ch 채널에 42 값을 보냄

채널로부터 값(데이터) 받기

채널로부터 값(데이터)를 받을 때도 <- 연산자를 사용합니다. 동일하게 이 연산자는 값이 전송될 때까지 고루틴을 블록(block)합니다.

value := <-ch // ch 채널로부터 값을 수신하고 value 변수에 저장

예제

package main

import (
    "fmt"
    "time"
)

func main() {
    ch := make(chan string)

    // 새로운 고루틴에서 메시지 보내기
    go func() {
        time.Sleep(2 * time.Second)
        ch <- "Hello, Channels!"
    }()

    // 채널로부터 메시지 수신
    msg := <-ch
    fmt.Println(msg)
}

위 코드에서는 메인 고루틴은 새로운 고루틴을 시작하고, 그 고루틴은 2초 후에 채널을 통해 메시지를 보냅니다. 메인 고루틴은 채널로부터 메시지를 수신하고 출력합니다.

채널의 버퍼링

채널은 2가지로 나뉘는데 첫째, Buffered Channel 둘째 Unbuffered Channel입니다.
Buffered Channel은 용량을 가지고 있으며, 채널이 가득 차지 않는 한 값을 보내는 고루틴이 블록되지 않습니다.

ch := make(chan int, 2) // 용량이 2인 버퍼링된 채널 생성
ch <- 1
ch <- 2
// ch <- 3 // 여기서 블록됨, 채널이 가득 찼기 때문
fmt.Println(<-ch)
fmt.Println(<-ch)

채널 닫기

채널을 닫으면 더 이상 그 채널에 값을 보낼 수 없습니다. 하지만 채널을 닫아도 채널에 남아 있는 값을 계속 수신할 수 있습니다. 채널이 닫히고 나서 모든 값을 수신한 후에 수신 연산은 채널 타입의 제로 값을 반환하게 됩니다.

package main

import "fmt"

func main() {
    ch := make(chan int, 3)
    
    // 채널에 값 보내기
    ch <- 1
    ch <- 2
    ch <- 3

    // 채널 닫기
    close(ch)

    // 채널로부터 값 수신
    for i := 0; i < 3; i++ {
        value, ok := <-ch
        fmt.Println(value, ok)
    }

    // 닫힌 채널에서 더 이상 값이 없을 때 수신
    value, ok := <-ch
    fmt.Println(value, ok) // 0 false
}
  1. 채널 ch를 생성 후 3개의 값을 보냅니다.
  2. 채널 닫기
  3. for문을 활용하여 채널로부터 값을 수신합니다. 값은 value에 저장되고, 채널이 닫혔는지 여부는 ok에 저장됩니다.
  4. 모든 값을 수신한 후, 닫힌 채널에서 값을 수신하려고 하면 value는 타입의 제로 값(int인 경우는 0)을 반환하고, ok는 false를 반환합니다.

위 코드와 다르게 range 키워드를 사용한 예제는 아래와 같습니다.

package main

import "fmt"

func main() {
    ch := make(chan int, 3)
    
    // 채널에 값 보내기
    ch <- 1
    ch <- 2
    ch <- 3

    // 채널 닫기
    close(ch)

    // range를 사용하여 채널로부터 값 수신
    for value := range ch {
        fmt.Println(value)
    }
}

위 코드에서는 range를 사용하여 닫힌 채널로 부터 남은 모든 값을 수신하고 채널이 닫히면 range 루프는 자동 종료됩니다.

채널 select 선택문

select문을 활용하여 복수 채널 연산을 기다릴 수 있습니다. 즉, select문은 여러 개의 case문에서 각각 다른 채널을 기다리다가 준비가 된 채널 case를 실행합니다. default문이 있다면 case문의 채널이 준비되지 않더라도 대기하지 않고 default문을 실행하지만, default문이 없다면 select문은 준비된 채널이 생길 때까지 블록(block)됩니다.

select {
case msg1 := <-ch1:
    fmt.Println("Received", msg1)
case msg2 := <-ch2:
    fmt.Println("Received", msg2)
default:
    fmt.Println("No messages received")
}
profile
Developer

0개의 댓글