19.2. 프로그램언어 고(Go)의 채널 닫기

프로그램언어 고(Go)에서의 채널 닫기 동작 이해하기

Go 언어의 채널 닫기 동작을 이해하는 것에 대해 설명드리겠습니다. Go 언어의 채널은 고루틴간 통신을 위한 통로 역할을 합니다.


ch := make(chan int)

위 코드에서 채널 ch를 만들었습니다.

채널을 닫으려면 built-in 함수 close를 사용합니다.


close(ch)

채널을 닫으면 더 이상의 데이터 전송은 불가능합니다. 하지만 계속해서 채널로부터 데이터를 읽을 수는 있습니다.


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

  go func() {
    ch <- 1
    ch <- 2
    close(ch)
  }()

  for i := range ch {
    fmt.Println(i)
  }
}

위 코드에서 고루틴이 채널 ch에 정수 1, 2를 보내고 채널을 닫았습니다. 메인 고루틴은 채널이 닫히기 전인 1, 2 데이터를 수신할 수 있습니다.

range 키워드를 사용하면 채널이 닫힐 때까지 계속해서 채널에서 데이터를 읽어올 수 있습니다.

채널 닫기의 주요 목적중 하나는 수신측에 채널 사용의 끝을 알리는 것입니다.

수신측에서 채널이 닫혔는지 확인하고 싶다면 다음처럼 코딩할 수 있습니다.


v, ok := <-ch 

ok 변수가 false면 채널이 닫힌 것입니다.

Go 언어의 채널 닫기 동작을 위와 같이 설명 드렸습니다. 예제와 함께 쉽고 자세히 설명드렸으니 참고하시기 바랍니다. 추가 문의사항 있으시면 언제든 질문 주세요.

프로그램언어 고(Go)에서의 닫힌 채널에서 데이터 받기

Go 언어에서 닫힌 채널(closed channel)은 더 이상 데이터를 보내지 않음을 의미합니다. 닫힌 채널에서는 데이터를 보내는 것은 허용되지 않지만, 데이터를 받는 것은 여전히 가능합니다.

닫힌 채널에서 데이터를 받는 방법은 다음과 같습니다.


package main

import "fmt"

func main() {

    c := make(chan int)
    
    // 채널 닫기
    close(c)
    
    // 닫힌 채널에서 값 받기
    v, ok := <-c
    if ok {
        fmt.Println(v)
    } else {
        fmt.Println("채널이 닫혔습니다") 
    }
}

위 예제에서는 int형 채널 c를 만듭니다. 그리고 바로 이 채널을 닫습니다.

닫힌 채널 c에서 <-c 표현식을 사용하여 값을 받습니다. 이때 받은 값은 v에, 성공 여부는 ok에 할당됩니다. ok가 true라면 값을 정상적으로 받은 것이고, false라면 채널이 닫혀서 더 이상 값을 받을 수 없다는 의미입니다. 따라서 닫힌 채널에서 값을 받을 때에는 반드시 이 ok 값을 체크하여 정상적으로 데이터를 받았는지 확인해야 합니다. 이처럼 Go 언어의 채널은 닫힌 이후에도 저장된 데이터를 받을 수 있게 해주므로 데이터 손실을 예방할 수 있습니다. 위 개념과 예제코드가 Go언어의 닫힌 채널에서 데이터 받기를 이해하는데 도움이 되었기를 바랍니다.

프로그램언어 고(Go)에서의 닫힌 채널에 데이터 보내기

Go 언어에서 닫힌 채널에 데이터를 보내려고 하면 패닉(panic)이 발생합니다.

채널은 기본적으로 보내기(send)와 받기(receive) 연산을 위해 사용되는 동기화 기법입니다. 보내기 연산이 성공하려면 반드시 받기 연산을 위한 대기자(receiver)가 있어야 합니다.

그렇지 않으면 보내기 연산이 블로킹(blocking)되어 프로그램 실행이 중단됩니다. 이를 방지하기 위해 Go 언어의 채널은 보내기 연산에 대한 별도의 버퍼(buffer)를 제공하는 buffered channel과 그렇지 않은 unbuffered channel으로 나뉩니다.

buffered channel의 경우에는 버퍼 용량만큼의 데이터를 보관할 수 있으므로 receiver가 없더라도 보내기 연산을 위한 버퍼링이 가능합니다. 반면 unbuffered channel은 별도의 버퍼가 없기 때문에 반드시 receiver가 준비되어 있어야만 보내기가 가능합니다.

그래서 닫힌 채널(closed channel)으로 보내기 연산을 시도하면 보내기를 위한 receiver가 없는 것으로 간주되어 패닉(panic)이 발생하게 됩니다.


package main

import "fmt"

func main() {

    // 닫힌 채널 만들기
    c := make(chan int)
    close(c)

    // 닫힌 채널에 데이터 보내기 시도
    c <- 1

    fmt.Println("Sent data on closed channel") 
}

위 예제 코드에서 채널 c를 먼저 닫았습니다. 그리고 나서 닫힌 채널 c에 데이터 1을 보내려고 시도했습니다.

이 경우 receiver가 없는 닫힌 채널이기 때문에 패닉(panic)이 발생하고 "Sent data on closed channel" 문장은 실행되지 않습니다.

즉, Go 언어의 채널은 닫히면 보내기만 닫히고 받기는 여전히 가능하다고 생각할 수 있습니다. 하지만 실제로는 닫힌 채널로의 보내기 자체가 불가능하므로 주의가 필요합니다.

닫힌 채널에 대한 보내기 시도는 프로그램 패닉을 유발하므로, 실무에서는 채널 사용 후 닫힘 여부를 확인하는 것이 좋습니다.

이상으로 Go 언어에서 닫힌 채널에 대한 데이터 보내기 동작을 설명드렸습니다.

프로그램언어 고(Go)에서의 range를 이용한 채널 데이터 접근

Go언어의 range를 이용하여 채널에서 데이터에 접근할 수 있습니다. 예를 들어 다음과 같은 코드가 있다고 합시다.

ch := make(chan int)

for i := 0; i < 5; i++ {
    ch <- i 
}

close(ch)

for v := range ch {
    fmt.Println(v) 
}

여기서 ch는 int 타입의 값을 보관하는 채널입니다. for 반복문을 사용하여 0부터 4까지의 정수를 ch 채널에 보냅니다.

그리고 ch 채널을 close() 함수로 닫습니다.

마지막으로 또 다른 for 반복문과 range 키워드를 이용하여 ch 채널로부터 데이터에 접근합니다.

range를 사용하면 닫힌 채널인 ch로부터 보낸 값을 하나씩 꺼내올 수 있습니다. 보낸 값의 개수만큼 반복하며 매 반복마다 v 변수에 현재 값이 할당됩니다.

이것을 fmt.Println() 함수로 출력하면 채널에 보낸 0, 1, 2, 3, 4 값이 차례대로 출력되는 것을 확인할 수 있습니다.

즉, Go 언어의 range를 사용하면 닫힌 채널에서 값을 읽어오는 것이 편리합니다. 보낸 값의 개수에 따라 자동으로 반복하면서 하나씩 접근할 수 있기 때문입니다.

range를 사용할 때는 반드시 채널이 닫혀있어야 합니다. 닫지 않으면 deadlock이 발생할 수 있습니다.

위 예제코드에 대한 더 자세한 설명은 다음과 같습니다.

1. ch 채널을 make() 함수로 생성합니다. 이는 int 타입의 데이터를 보관할 수 있는 채널을 만드는 것입니다.

2. 첫 번째 for문에서 0부터 4까지의 숫자를 ch 채널에 보냅니다. <- 연산자를 사용하여 값을 보냅니다. 3. 채널 사용이 끝났으므로 close() 함수로 닫습니다. 4. 두 번째 for문에서 range와 함께 ch 채널을 순회합니다. 이로써 채널에 기록된 값을 차례대로 읽어올 수 있습니다. 5. v 변수에 읽어온 값이 할당되므로 이를 출력합니다. 이러한 방식으로 Go 언어의 range와 채널을 이용하면 코드의 반복을 피하고 효율적으로 데이터에 접근할 수 있습니다. 답변 드렸습니다. 도움이 되었기를 바랍니다.

프로그램언어 고(Go)에서의 닫힌 채널과 panic 처리

Go 언어에서 닫힌 채널(closed channel)은 더 이상 값을 보내거나 받을 수 없는 채널을 말합니다. 채널을 닫으면 해당 채널로의 send 동작은 허용되지 않고, receive 동작만 가능합니다.


package main

import "fmt"

func main() {

    c := make(chan int)
    
    close(c)
    
    // 닫힌 채널로 send하려고 하면 panic 발생
    c <- 1 
}

위의 예제코드에서 채널 c를 닫았습니다. 이후에 닫힌 채널 c로 값 1을 보내려고 하면 panic이 발생합니다.

panic은 Go 언어의 런타임 에러입니다. 복구할 수 없는 심각한 에러가 발생했을 때 panic이 일어납니다. 위 예제에서 닫힌 채널로 값을 보내려고 해서 발생한 panic 입니다.

panic이 발생하면 해당 고루틴이 즉시 중단되고, defer 구문이 실행됩니다. 그리고 연쇄 반응으로 연관된 다른 고루틴들도 모두 중단될 수 있습니다.

panic 발생 시에는 복구/처리 코드를 작성하는 것이 좋습니다. 아래는 panic 발생 시 복구 코드 예제입니다.


package main

import "fmt"

func recovery() {
    if r := recover(); r != nil {
        fmt.Println("Panic occurred but recovered") 
    }
}

func main() {

    defer recovery()
    
    c := make(chan int)
    close(c)
    c <- 1
} 

main 함수에서 defer로 recovery() 함수를 실행했습니다. recovery() 함수 내에서 recover() 내장 함수를 사용하여 panic 발생 시 nil이 아닌 값을 반환받고, "Panic occurred but recovered" 라는 메시지를 출력하는 방식으로 복구 코드를 작성하였습니다.

이렇게 panic 발생에 대한 복구 코드를 작성하는 것이 안전합니다.

답변이 도움이 되었기를 바랍니다.

Leave a Comment