0Pricing
Go Academy · 강의

버퍼 채널

용량, 채널 닫기 및 채널에 대한 range

버퍼 채널은(는) CoddyKit의 무료 Go Academy 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Go Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Go Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

버퍼가 있는 채널이란 무엇인가요

버퍼가 있는 채널에는 용량이 있으므로 버퍼에 여유 공간이 있는 동안에는 송신이 차단되지 않습니다. 채널은 대기열처럼 동작합니다:

package main
import "fmt"

func main() {
    ch := make(chan int, 3)  // capacity 3

    ch <- 1  // does not block — buffer has room
    ch <- 2  // does not block
    ch <- 3  // does not block
    // ch <- 4  // would block — buffer full

    fmt.Println(<-ch) // 1
    fmt.Println(<-ch) // 2
    fmt.Println(<-ch) // 3
}

대기열로서의 버퍼가 있는 채널

버퍼가 있는 채널은 goroutine 사이에 FIFO 대기열을 구현합니다:

package main
import ("fmt"; "sync")

func producer(ch chan<- int, wg *sync.WaitGroup) {
    defer wg.Done()
    for i := 1; i <= 5; i++ {
        ch <- i
        fmt.Printf("produced %d\n", i)
    }
}

func main() {
    ch := make(chan int, 2)  // buffer 2 — producer runs ahead
    var wg sync.WaitGroup
    wg.Add(1)
    go producer(ch, &wg)
    wg.Wait()
    close(ch)
    for v := range ch { fmt.Printf("consumed %d\n", v) }
}

채널에서 len()과 cap() 사용하기

버퍼가 있는 채널의 현재 크기와 용량을 확인합니다:

package main
import "fmt"

func main() {
    ch := make(chan string, 5)
    ch <- "a"
    ch <- "b"
    ch <- "c"

    fmt.Println("cap:", cap(ch))  // 5
    fmt.Println("len:", len(ch))  // 3 — items currently buffered

    <-ch
    fmt.Println("len:", len(ch))  // 2
}

생산자와 소비자 분리

버퍼는 producer의 속도와 consumer의 속도를 분리합니다. producer는 차단되기 전에 버퍼 크기만큼 앞서 실행할 수 있습니다:

package main
import ("fmt"; "sync"; "time")

func produce(ch chan<- int, wg *sync.WaitGroup) {
    defer wg.Done()
    for i := 0; i < 5; i++ {
        ch <- i
    }
}

func consume(ch <-chan int, wg *sync.WaitGroup) {
    defer wg.Done()
    for v := range ch {
        time.Sleep(10 * time.Millisecond)  // slower consumer
        fmt.Println("consumed:", v)
    }
}

func main() {
    ch := make(chan int, 3)
    var wg sync.WaitGroup
    wg.Add(2)
    go produce(ch, &wg)
    go consume(ch, &wg) // BUG: produce closes before consume sees all
    // In practice, coordinate close carefully
    fmt.Println("decoupled")
}

버퍼가 있는 채널을 이용한 세마포어

용량이 N인 버퍼가 있는 채널은 동시에 실행되는 goroutine 수를 N개로 제한할 수 있습니다. 간단한 세마포어로 사용할 수 있습니다:

package main
import ("fmt"; "sync"; "time")

func main() {
    sem := make(chan struct{}, 3)  // max 3 concurrent
    var wg sync.WaitGroup

    for i := 0; i < 10; i++ {
        wg.Add(1)
        i := i
        go func() {
            defer wg.Done()
            sem <- struct{}{}    // acquire
            defer func() { <-sem }()  // release
            fmt.Printf("job %d running\n", i)
            time.Sleep(50 * time.Millisecond)
        }()
    }
    wg.Wait()
}

버퍼가 있는 채널 닫기

버퍼가 있는 채널을 닫으면 수신자가 닫힘을 감지하기 전에 버퍼에 남아 있는 값을 모두 꺼낼 수 있습니다:

package main
import "fmt"

func main() {
    ch := make(chan int, 5)
    for i := 1; i <= 3; i++ { ch <- i }
    close(ch)

    // Drain all buffered values after close:
    for v := range ch {
        fmt.Println(v)  // 1, 2, 3
    }
    // range exits after channel closed and drained
}

버퍼 유무 선택 기준

선택 기준입니다:

  • 버퍼 없음: 확실한 동기적 인계, 엄격한 순서 보장, 동기적 만남을 통한 동기화
  • 버퍼 있음: producer와 consumer의 속도 분리, 차단 감소, 세마포어 구현, N개의 결과 수집
  • 기본값은 버퍼 없음으로 하고, 크기를 정당화할 수 있을 때만 버퍼를 추가합니다
  • 버퍼 크기가 너무 작으면 → 여전히 차단되고, 너무 크면 → 버그가 숨겨집니다

버퍼가 있는 채널로 결과 수집

버퍼가 있는 채널을 사용하면 병렬로 실행되는 goroutine의 결과를 차단 없이 수집할 수 있습니다:

package main
import ("fmt"; "sync")

func square(n int) int { return n * n }

func main() {
    nums := []int{1, 2, 3, 4, 5}
    results := make(chan int, len(nums))  // buffer = goroutine count
    var wg sync.WaitGroup

    for _, n := range nums {
        wg.Add(1)
        n := n
        go func() {
            defer wg.Done()
            results <- square(n)
        }()
    }
    wg.Wait()
    close(results)

    for r := range results { fmt.Println(r) }
}

가득 찬 버퍼로 인한 goroutine 누수

버퍼가 가득 차 있고 읽는 쪽이 없으면 송신자가 영원히 차단되어 goroutine 누수가 발생합니다:

package main
import ("fmt"; "time"; "runtime")

func main() {
    ch := make(chan int, 2)
    // Start goroutine that fills buffer and then blocks
    go func() {
        ch <- 1
        ch <- 2
        ch <- 3  // blocks: buffer full, no receiver
        fmt.Println("never reached")
    }()
    time.Sleep(50 * time.Millisecond)
    fmt.Println("goroutines:", runtime.NumGoroutine()) // 2 — leaked
}

버퍼가 있는 채널로서의 time.After

time.After는 용량이 1인 버퍼가 있는 <-chan time.Time을 반환합니다. 런타임은 지정된 기간 후 해당 채널로 값을 보내고, 그 후 채널은 가비지 컬렉션됩니다:

package main
import ("fmt"; "time")

func doWithTimeout(work func() string) (string, bool) {
    result := make(chan string, 1)
    go func() { result <- work() }()
    select {
    case r := <-result:
        return r, true
    case <-time.After(100 * time.Millisecond):
        return "", false
    }
}

func main() {
    r, ok := doWithTimeout(func() string {
        time.Sleep(50 * time.Millisecond)
        return "done"
    })
    fmt.Println(r, ok)
}

빠른 확인

용량이 가득 찬 버퍼가 있는 채널로 송신하면 어떻게 되나요?

복습: 버퍼가 있는 채널

요약:

  • make(chan T, N)은 용량이 N인 버퍼가 있는 채널을 생성합니다
  • 버퍼가 가득 찼을 때만 송신이 차단되고, 버퍼가 비었을 때만 수신이 차단됩니다
  • len(ch) — 현재 항목 수, cap(ch) — 용량
  • producer와 consumer를 분리하고, 병렬 결과를 수집하며, 세마포어를 구현할 때 사용합니다
  • 닫으면 EOF 전에 버퍼를 비울 수 있으며, range ch가 이를 자동으로 처리합니다

자주 묻는 질문

“버퍼 채널” 강의는 무료인가요?

네 — “버퍼 채널” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Go Academy 강의 전체를 잠금 해제할 수 있습니다. Go Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“버퍼 채널”에서 뭘 배우나요?

용량, 채널 닫기 및 채널에 대한 range 브라우저에서 직접 실행하는 실습 코드로 Go Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Go Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Go Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.

“버퍼 채널” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Go Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Go Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 고루틴 실행
  2. 비버퍼 채널
  3. 버퍼 채널
  4. 채널 방향과 파이프라인 패턴
← Go Academy(으)로 돌아가기