What is the neatest idiom for producer/consumer in Go?

后端 未结 5 2065
不知归路
不知归路 2021-02-01 23:42

What I would like to do is have a set of producer goroutines (of which some may or may not complete) and a consumer routine. The issue is with that caveat in parentheses - we do

5条回答
  •  失恋的感觉
    2021-02-02 00:23

    I'm adding this because the extant answers don't make a couple things clear. First, the range loop in the codewalk example is just an infinite event loop, there to keep re-checking and updating the same url list forever.

    Next, a channel, all by itself, already is the idiomatic consumer-producer queue in Go. The size of the async buffer backing the channel determines how much producers can produce before getting backpressure. Set N = 0 below to see lock-step producer consumer without anyone racing ahead or getting behind. As it is, N = 10 will let the producer produce up to 10 products before blocking.

    Last, there are some nice idioms for writing communicating sequential processees in Go (e.g. functions that start go routines for you, and using the for/select pattern to communicate and accept control commands). I think of WaitGroups as clumsy, and would like to see idiomatic examples instead.

    package main
    
    import (
        "fmt"
        "time"
    )
    
    type control int
    const  (
        sleep control = iota
        die // receiver will close the control chan in response to die, to ack.
    )
    
    func (cmd control) String() string {
        switch cmd {
        case sleep: return "sleep"
        case die: return "die"
        }
        return fmt.Sprintf("%d",cmd)
    }
    
    func ProduceTo(writechan chan<- int, ctrl chan control, done chan bool) {
        var product int
        go func() {
            for {
                select {
            case writechan <- product:
                fmt.Printf("Producer produced %v\n", product)
                product++
            case cmd:= <- ctrl:
                fmt.Printf("Producer got control cmd: %v\n", cmd)
                switch cmd {
                case sleep:
                    fmt.Printf("Producer sleeping 2 sec.\n")
                    time.Sleep(2000 * time.Millisecond)
                case die:
                    fmt.Printf("Producer dies.\n")
                    close(done)
                    return
                }
                }
            }
        }()
    }
    
    func ConsumeFrom(readchan <-chan int, ctrl chan control, done chan bool) {
        go func() {
            var product int
            for {
                select {
                case product = <-readchan:
                    fmt.Printf("Consumer consumed %v\n", product)
                case cmd:= <- ctrl:
                    fmt.Printf("Consumer got control cmd: %v\n", cmd)
                    switch cmd {
                    case sleep:
                        fmt.Printf("Consumer sleeping 2 sec.\n")
                        time.Sleep(2000 * time.Millisecond)
                    case die:
                        fmt.Printf("Consumer dies.\n")
                        close(done)
                        return
                    }
    
                }
            }
        }()
    }
    
    func main() {
    
        N := 10
        q := make(chan int, N)
    
        prodCtrl := make(chan control)
        consCtrl := make(chan control)
    
        prodDone := make(chan bool)
        consDone := make(chan bool)
    
    
        ProduceTo(q, prodCtrl, prodDone)
        ConsumeFrom(q, consCtrl, consDone)
    
        // wait for a moment, to let them produce and consume
        timer := time.NewTimer(10 * time.Millisecond)
        <-timer.C
    
        // tell producer to pause
        fmt.Printf("telling producer to pause\n")
        prodCtrl <- sleep
    
        // wait for a second
        timer = time.NewTimer(1 * time.Second)
        <-timer.C
    
        // tell consumer to pause
        fmt.Printf("telling consumer to pause\n")
        consCtrl <- sleep
    
    
        // tell them both to finish
        prodCtrl <- die
        consCtrl <- die
    
        // wait for that to actually happen
        <-prodDone
        <-consDone
    }
    

提交回复
热议问题