go - 检查我是否可以从 channel 读取

标签 go concurrency channel goroutine

package main

import (
    "fmt"
    "strconv"
    "time"
)

func generator() chan int {
    ch := make(chan int)
    go func() {
        i := 0
        for {
            i++
            ch <- i
            time.Sleep(time.Duration(10) * time.Millisecond)
        }
    }()
    return ch
}

func printer(delay int, square bool, ch chan int) {
    for n := range ch {
        if (square) {
            fmt.Printf("["+strconv.Itoa(n) + "],")
        } else {
            fmt.Printf("("+strconv.Itoa(n) + "),")
        }
        time.Sleep(time.Duration(delay) * time.Millisecond)
    }
}

func sendToBoth(ch chan int) (ch1 chan int, ch2 chan int) {
    ch1 = make(chan int)
    ch2 = make(chan int)
    go func() {
        for {
            //n := <- ch
            select {
                case v := <- ch: //this is the problem point
                    ch1  <- v    //
                case v := <- ch: //
                    ch2 <- v     //
            }
        }
    }()
    return ch1, ch2
}

func main() {
    ch1, ch2 := sendToBoth(generator())
    go printer(100, true, ch1) //[]
    go printer(200, false, ch2) //()
    var name string
    fmt.Scan(&name)
}
我想实现sendToBoth函数,该函数从ch channel 获取生成的数字1,2,3,...,并将其发送到ch1ch2。但是每个都有不同的延迟,并且我不想让一个人等待其他人解锁,因此我尝试使用select,但无法弄清楚如何查询case子句中是否存在ch1ch2。有什么帮助吗?
输出应该像
(1),[1],[2],(2),[3],[4],(3),[5],[6],(4),[7],[8],...

最佳答案

因此,我要说的第一个 react 是“只要让他们同步运行,就会容易得多。”

func sendToBoth(ch chan int) (ch1, ch2 chan int) {
    ch1 = make(chan int)
    ch2 = make(chan int)
    go func() {
        defer close(ch1)
        defer close(ch2)
        for n := range ch {
            ch1 <- n
            ch2 <- n
        }
    }()
    return ch1, ch2
}
如此简单,我喜欢它!但是,假设您希望ch1和ch2各自消耗自己的速率。如果您希望它们彼此分开,则必须使用临时存储来解决。最简单的方法是给 channel 一些缓冲空间:
ch1 = make(chan int, 10)
ch2 = make(chan int, 10)
现在,ch1可以运行得更快-但它只能比ch2提前10个项目。或相反亦然。
如果要使用无限大小的缓冲区,则必须自己保留。我们可以利用nil channel 完全可以用作select分支这一事实:
func sendToBoth(ch chan int) (ch1, ch2 chan int) {
    ch1 = make(chan int)
    ch2 = make(chan int)
    go func() {
        defer close(ch1)
        defer close(ch2)
        var arr []int
        var pos1, pos2 int
        ich := ch
        for inch != nil && (pos1 < len(arr) || pos2 < len(arr)) {
            var och1, och2 chan int
            var v1, v2 int
            if pos1 < len(arr) {
                och1 = ch1
                v1 = arr[pos1]
            }
            if pos2 < len(arr) {
                och2 = ch2
                v2 = arr[pos2]
            }
            select {
            case n, ok := <- ich:
                if !ok {
                    ich = nil // done
                } else {
                    arr = append(arr, n)
                }
            case och1 <- v1:
                pos1++
            case och2 <- v2:
                pos2++
            }
        }
    }()
    return ch1, ch2
}
稍微复杂一点-我不完全确定这是正确的。另请注意,没有努力释放流中旧项目的存储空间。

关于go - 检查我是否可以从 channel 读取,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64231097/

相关文章:

c# - 在多线程场景中从 List<T> 添加/删除特定项目的最佳方法是什么

Java并发实践: BoundedExecutor implementation

转到 : channel is necessary in this case?

concurrency - 在函数中将 channel 作为参数传递的不同方法

go - 如何在同一循环内向 channel 发送值或从 channel 接收值?

go - 如何使用 Gorm 将外键添加到模型中

validation - 如何根据 http 方法类型设置不同的验证约束?

java - Callable 执行期间出现异常

go - 两个字符串具有相同的指针地址

go - 连接到远程 golang 服务器