수색…


소개

채널은 주어진 유형의 값을 포함합니다. 값은 채널에 쓰고 읽을 수 있으며 선입 선출 순서로 채널 내부를 순환합니다. 여러 메시지를 포함 할 수있는 버퍼링 된 채널과 버퍼링되지 않은 채널은 구분할 수 없습니다. 채널은 일반적으로 goroutine 간의 통신에 사용되지만 다른 상황에서도 유용합니다.

통사론

  • make (chan int) // 버퍼되지 않은 채널 생성
  • make (chan int, 5) // 용량이 5 인 버퍼링 된 채널을 만든다.
  • close (ch) // 채널 "ch"를 닫습니다.
  • ch <- 1 // 채널 "ch"에 1의 값을 쓴다.
  • val : = <-ch // 채널 "ch"에서 값을 읽습니다.
  • val, ok : = <-ch // 대체 구문; ok는 채널이 닫혀 있는지를 나타내는 bool입니다.

비고

빈 구조체 make(chan struct{}) 를 담고있는 채널은 사용자에게 채널을 통해 정보가 전송되지 않고 순수하게 동기화에 사용된다는 명확한 메시지입니다.

버퍼링되지 않은 채널의 경우 채널 쓰기는 다른 goroutine에서 해당 읽기가 발생할 때까지 차단됩니다. 라이터를 기다리는 동안 채널 읽기 차단도 마찬가지입니다.

범위 사용

한 채널에서 여러 값을 읽을 때 range 를 사용하는 것이 일반적인 패턴입니다.

func foo() chan int {
    ch := make(chan int)
    
    go func() {
        ch <- 1
        ch <- 2
        ch <- 3
        close(ch)

    }()
    
    return ch
}

func main() {    
    for n := range foo() {
        fmt.Println(n)
    }
    
    fmt.Println("channel is now closed")
}

운동장

산출

1
2
3
channel is now closed

시간 초과

채널은 종종 타임 아웃을 구현하는 데 사용됩니다.

func main() {
    // Create a buffered channel to prevent a goroutine leak. The buffer
    // ensures that the goroutine below can eventually terminate, even if
    // the timeout is met. Without the buffer, the send on the channel
    // blocks forever, waiting for a read that will never happen, and the
    // goroutine is leaked.
    ch := make(chan struct{}, 1)

    go func() {
        time.Sleep(10 * time.Second)
        ch <- struct{}{}
    }()

    select {
    case <-ch:
        // Work completed before timeout.
    case <-time.After(1 * time.Second):
        // Work was not completed after 1 second.
    }
}

코디네이션 용 goroutines

메인 스레드가 각 단계 사이에 어떤 작업을해야하는 2 단계 프로세스로 goroutine을 상상해보십시오.

func main() {
    ch := make(chan struct{})
    go func() {
        // Wait for main thread's signal to begin step one
        <-ch
        
        // Perform work
        time.Sleep(1 * time.Second)
        
        // Signal to main thread that step one has completed
        ch <- struct{}{}
        
        // Wait for main thread's signal to begin step two
        <-ch
        
        // Perform work
        time.Sleep(1 * time.Second)
        
        // Signal to main thread that work has completed
        ch <- struct{}{}
    }()
    
    // Notify goroutine that step one can begin
    ch <- struct{}{}
    
    // Wait for notification from goroutine that step one has completed
    <-ch

    // Perform some work before we notify
    // the goroutine that step two can begin
    time.Sleep(1 * time.Second)
    
    // Notify goroutine that step two can begin
    ch <- struct{}{}
    
    // Wait for notification from goroutine that step two has completed
    <-ch
}

버퍼링되지 않은 vs 버퍼되지 않은

func bufferedUnbufferedExample(buffered bool) {
    // We'll declare the channel, and we'll make it buffered or
    // unbuffered depending on the parameter `buffered` passed
    // to this function.
    var ch chan int
    if buffered {
        ch = make(chan int, 3)
    } else {
        ch = make(chan int)
    }
    
    // We'll start a goroutine, which will emulate a webserver
    // receiving tasks to do every 25ms.
    go func() {
        for i := 0; i < 7; i++ {
            // If the channel is buffered, then while there's an empty
            // "slot" in the channel, sending to it will not be a
            // blocking operation. If the channel is full, however, we'll
            // have to wait until a "slot" frees up.
            // If the channel is unbuffered, sending will block until
            // there's a receiver ready to take the value. This is great
            // for goroutine synchronization, not so much for queueing
            // tasks for instance in a webserver, as the request will
            // hang until the worker is ready to take our task.
            fmt.Println(">", "Sending", i, "...")
            ch <- i
            fmt.Println(">", i, "sent!")
            time.Sleep(25 * time.Millisecond)
        }
        // We'll close the channel, so that the range over channel
        // below can terminate.
        close(ch)
    }()
    
    for i := range ch {
        // For each task sent on the channel, we would perform some
        // task. In this case, we will assume the job is to
        // "sleep 100ms".
        fmt.Println("<", i, "received, performing 100ms job")
        time.Sleep(100 * time.Millisecond)
        fmt.Println("<", i, "job done")
    }
}

운동장에 가라.

채널 차단 및 차단 해제

기본적으로 채널을 통한 통신은 동기화입니다. 당신이 어떤 가치를 보낼 때 수신기가 있어야합니다. 그렇지 않으면 fatal error: all goroutines are asleep - deadlock! 다음과 같이

package main

import "fmt"

func main() {
    msg := make(chan string)
    msg <- "Hey There"
    go func() {
        fmt.Println(<-msg)
    }()
}

부 솔루션 사용 : 버퍼 된 채널 사용 :

package main

import "fmt"
import "time"

func main() {
    msg :=make(chan string, 1)
    msg <- "Hey There!"
    go func() {
        fmt.Println(<-msg)
    }()
    time.Sleep(time.Second * 1)
}

작업 완료를 기다리는 중입니다.

채널을 사용하는 일반적인 기법은 채널에서 읽을 수있는 작업자 (또는 소비자)를 만드는 것입니다. sync.WaitGroup을 사용하면 작업자가 실행을 마칠 때까지 기다릴 수있는 쉬운 방법입니다.

package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {
    numPiecesOfWork := 20
    numWorkers := 5

    workCh := make(chan int)
    wg := &sync.WaitGroup{}

    // Start workers
    wg.Add(numWorkers)
    for i := 0; i < numWorkers; i++ {
        go worker(workCh, wg)
    }

    // Send work
    for i := 0; i < numPiecesOfWork; i++ {
        work := i % 10 // invent some work
        workCh <- work
    }

    // Tell workers that no more work is coming
    close(workCh)

    // Wait for workers to finish
    wg.Wait()

    fmt.Println("done")
}

func worker(workCh <-chan int, wg *sync.WaitGroup) {
    defer wg.Done() // will call wg.Done() right before returning

    for work := range workCh { // will wait for work until workCh is closed
        doWork(work)
    }
}

func doWork(work int) {
    time.Sleep(time.Duration(work) * time.Millisecond)
    fmt.Println("slept for", work, "milliseconds")
}


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow