ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • golang: 채널(Channel)이란?
    Back-End/Golang 2024. 3. 30. 12:10
    반응형

    채널(Channel)은 Go 언어의 핵심 기능 중 하나로, 고루틴 간에 데이터를 안전하게 주고받을 수 있는 통신 메커니즘입니다. 채널을 사용하면 고루틴 사이에서 동기화 없이 데이터를 전달할 수 있으며, 이는 동시성 프로그래밍에서 발생할 수 있는 복잡한 문제들을 효과적으로 해결할 수 있게 해줍니다.

    채널의 특징:

    • 타입 안전성: 채널은 특정 타입의 데이터만을 전송할 수 있으며, 이는 프로그램의 안정성을 높여줍니다.
    • 동기화: 채널을 통해 데이터를 보내거나 받을 때, Go 런타임은 이 과정이 안전하게 이루어지도록 자동으로 동기화를 관리합니다.
    • 블로킹과 넌블로킹 동작: 채널은 기본적으로 블로킹(Blocking) 동작을 합니다. 데이터를 보내거나 받을 준비가 되지 않았을 경우, 해당 고루틴은 대기 상태에 머물게 됩니다. 넌블로킹(Non-blocking) 동작은 select문을 사용하여 구현할 수 있습니다.

    기본 채널 사용 예시:

    아래 예제는 간단한 채널 사용법을 보여줍니다. 이 예제에서는 한 고루틴이 숫자를 채널에 보내고, 메인 고루틴이 그 숫자를 받아 출력합니다.

    package main
    
    import "fmt"
    
    func main() {
        // int 타입을 전송할 수 있는 채널 생성
        messageChannel := make(chan int)
    
        // 고루틴에서 채널을 통해 숫자 1을 보냄
        go func() {
            messageChannel <- 1
        }()
    
        // 메인 고루틴에서 채널로부터 숫자를 받아 출력
        message := <-messageChannel
        fmt.Println("Received:", message)
    }

     

    버퍼링된 채널 사용 예시:

    채널은 버퍼링 옵션을 제공하여, 채널이 가득 차지 않는 한 블로킹 없이 데이터를 보내고 받을 수 있게 합니다. 버퍼링된 채널의 사용 예를 아래에 보여드립니다.

    package main
    
    import "fmt"
    
    func main() {
        // 버퍼 크기가 2인 채널 생성
        channel := make(chan int, 2)
    
        // 채널에 데이터를 보냄 (블로킹 되지 않음)
        channel <- 1
        channel <- 2
    
        // 채널로부터 데이터를 받아옴 (블로킹 되지 않음)
        fmt.Println(<-channel)
        fmt.Println(<-channel)
    }

    이 예제에서는 채널에 두 개의 데이터를 연속으로 보내도 블로킹되지 않습니다. 이는 채널이 버퍼링되어 있기 때문입니다. 버퍼링된 채널은 비동기적인 작업을 관리할 때 유용하게 사용될 수 있습니다.

    채널을 사용하는 것은 Go에서 동시성을 다룰 때 매우 중요하며, 고루틴 간에 안전하고 효율적인 데이터 교환 방법을 제공합니다. 초기 단계에서 채널의 개념을 잘 이해하고 활용하는 것이 Go 언어를 사용하여 강력하고 효율적인 동시성 프로그램을 작성하는 데 도움이 됩니다.

     

    채널과 select 활용(넌블로킹) 예시:

    select 문은 Go에서 여러 채널의 동작을 동시에 기다리며, 준비된 채널의 경우에만 실행을 진행하는 방식으로 넌블로킹(Non-blocking) 동작을 구현할 수 있게 합니다. 이를 통해, 한 고루틴에서 여러 채널에 대한 입출력을 효율적으로 관리할 수 있습니다.

    select 문의 각 케이스는 특정 채널에서의 send 또는 receive 작업과 연결됩니다. 준비된(즉, 바로 수행 가능한) 케이스가 있으면, select는 그 중 하나를 무작위로 선택해 실행합니다. 만약 어떤 케이스도 준비되지 않았다면, default 케이스(있을 경우)가 실행되어 블로킹을 방지합니다.

    넌블로킹 I/O 예시

    아래 예제는 select 문을 사용하여 넌블로킹 방식으로 채널에서 데이터를 수신하는 방법을 보여줍니다.

    package main
    
    import (
    	"fmt"
    	"time"
    )
    
    func main() {
    	// 버퍼 없는 채널 생성
    	ch := make(chan int)
    	quit := make(chan bool)
    
    	// 별도의 고루틴에서 채널에 데이터를 보내는 시뮬레이션
    	go func() {
    		time.Sleep(2 * time.Second)
    		ch <- 1
    		quit <- true
    	}()
    
    	for {
    		select {
    		case num := <-ch: // ch 채널에서 데이터를 수신할 준비가 되었을 때
    			fmt.Println("Received:", num)
    		case <-time.After(1 * time.Second): // 1초 동안 다른 케이스가 준비되지 않으면 실행
    			fmt.Println("Timeout! No data received.")
    		case <-quit: // quit 채널에서 종료 신호를 받으면 루프 종료
    			fmt.Println("Quitting.")
    			return
    		}
    	}
    }

    이 예제에서는 세 가지 케이스를 사용합니다:

    • 첫 번째 케이스는 ch 채널에서 데이터를 받는 것입니다. 데이터가 도착하면 출력합니다.
    • 두 번째 케이스는 time.After 함수를 사용하여 특정 시간이 지난 후 실행되는 타임아웃 기능을 구현합니다. 이는 1초 동안 ch 채널에서 데이터가 수신되지 않으면 실행됩니다.
    • 세 번째 케이스는 quit 채널을 통해 종료 신호를 받는 것입니다. 이 신호가 수신되면 프로그램은 루프에서 빠져나와 종료합니다.

    select 문을 사용함으로써, 프로그램은 여러 채널에 대해 동시에 대기하면서도, 특정 채널에서의 데이터 수신이 지연되거나 발생하지 않아도 계속해서 실행을 이어갈 수 있습니다. 이는 고루틴이 블로킹 되지 않도록 하여, 더 반응성이 높고 효율적인 프로그램을 만드는 데 도움이 됩니다.

    반응형

    'Back-End > Golang' 카테고리의 다른 글

    golang: for-select 패턴  (0) 2024.04.19
    golang: 동시성에서 제한(Confinement)이란?  (0) 2024.04.19
    golang: 포인터(Pointer)란?  (0) 2024.03.30
    golang: 컨텍스트(Context)란?  (0) 2024.03.30
    golang: 고루틴(Goroutines)이란?  (0) 2024.03.30
Designed by Tistory.