go - 如何编写更好的双 channel 选择

标签 go goroutine

在下面的代码中有两个包含工作的 channel A 和 B,在实际代码中它们是不同的结构,工作人员在退出前需要排空两个 channel 。工作人员需要来自两个 channel 的信息。这两个 select 语句有效,但非常笨拙。如果我添加 default: 使它们成为非阻塞的,那么代码将无法耗尽 channel 。有没有更好的方式来编写选择?

现在,如果 channel A 没有工作,那么 channel B 也不会得到服务。另一个需要解决的问题,但不是我主要关心的问题。

playground用于测试以下代码:

package main

import (
    "fmt"
    "time"
)

const (
    fillCount  = 10 // number of elements in each input channel
    numWorkers = 3  // number of consumers.
)

func Wait() {
    time.Sleep(2000 * time.Millisecond)
}

func fillChannel(work chan string, name string) {
    for i := 0; i < fillCount; i++ {
        work <- fmt.Sprintf("%s%d", name, i)
    }
    close(work) // we're finished
}

func doWork(id int, ch1 chan string, ch2 chan string, done chan bool) {
    fmt.Println("Running worker", id)
    defer fmt.Println("Ending worker", id)

    for ch1Open, ch2Open := true, true; ch1Open && ch2Open; {
        cnt1 := len(ch1)
        cnt2 := len(ch2)

        if ch1Open {
            select {
            case str, more := <-ch1:
                if more {
                    fmt.Printf("%d: ch1(%d) %s\n", id, cnt1, str)
                } else {
                    fmt.Printf("%d: ch1 closed\n", id)
                    ch1Open = false
                }
            }
        }

        if ch2Open {
            select {
            case str, more := <-ch2:
                if more {
                    fmt.Printf("%d: ch2(%d) %s\n", id, cnt2, str)
                } else {
                    fmt.Printf("%d: ch2 closed\n", id)
                    ch2Open = false
                }
            }
        }
    }
    done <- true
}

func main() {

    a := make(chan string, 2) // a small channel
    b := make(chan string, 5) // a bigger channel

    // generate work
    go fillChannel(a, "A")
    go fillChannel(b, "B")

    // launch the consumers
    done := make(chan bool)

    for i := 0; i < numWorkers; i++ {
        go doWork(i, a, b, done)
    }

    // wait for the goroutines to finish.
    for i := 0; i < numWorkers; i++ {
        <-done
    }
    fmt.Println("All workers done.")

    Wait() // without this the defered prints from the workers doesn't flush
}

最佳答案

循环选择两个 channel 。当 channel 关闭时,将 channel 变量设置为 nil 以使该 channel 上的接收未就绪。当两个 channel 都为零时跳出循环。

http://play.golang.org/p/9gRY1yKqJ9

package main

import (
    "fmt"
    "time"
)

const (
    fillCount  = 10 // number of elements in each input channel
    numWorkers = 3  // number of consumers.
)

func fillChannel(work chan string, name string) {
    for i := 0; i < fillCount; i++ {
        work <- fmt.Sprintf("%s%d", name, i)
    }
    close(work) // we're finished
}

func doWork(id int, ch1 chan string, ch2 chan string, done chan bool) {
    fmt.Println("Running worker", id)
    for ch1 != nil || ch2 != nil {
        select {
        case str, ok := <-ch1:
            if ok {
                fmt.Printf("%d: ch1(%d) %s\n", id, len(ch1), str)
            } else {
                ch1 = nil
                fmt.Printf("%d: ch1 closed\n", id)
            }

        case str, ok := <-ch2:
            if ok {
                fmt.Printf("%d: ch2(%d) %s\n", id, len(ch2), str)
            } else {
                ch2 = nil
                fmt.Printf("%d: ch2 closed\n", id)
            }

        }
    }
    fmt.Println("Ending worker", id)
    done <- true
}

func main() {

    a := make(chan string, 2) // a small channel
    b := make(chan string, 5) // a bigger channel

    // generate work
    go fillChannel(a, "A")
    go fillChannel(b, "B")

    // launch the consumers
    done := make(chan bool)

    for i := 0; i < numWorkers; i++ {
        go doWork(i, a, b, done)
    }

    // wait for the goroutines to finish.
    for i := 0; i < numWorkers; i++ {
        <-done
    }
    fmt.Println("All workers done.")
}

关于go - 如何编写更好的双 channel 选择,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25917250/

相关文章:

bash - MINGW64 "make build"错误 : "bash: make: command not found"

multithreading - 使用 goroutine 运行同一程序的另一个实例?

go - 为什么 main goroutine 总是第二个被调用

json - 是否有(惯用或任何其他)方法将附加上下文/状态传递给嵌套结构的 UnmarshalJSON([]byte) 接口(interface)实现?

go - 关闭由 channel 链接的 goroutine 链的优雅方法是什么?

正则表达式获取空格后的最后一项

Golang Base64 到 Hex 转换

go - 如何关闭所有 "asleep"的 goroutines?

Go routine 不接收通过 channel 发送的所有数据——玩具示例程序

go - 如何使用 channel 和 goroutines 构建 Go Web 服务器?