检查我是否可以从频道读取

问题描述

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获取生成的数字1,2,3,...并将其发送到ch1ch2。但是每个人都有不同的延迟,因此我不想一个人等待其他人解锁,所以我尝试使用select,但不知道如何询问ch1或{{ 1}}目前在case子句中可用。有什么帮助吗? 输出应该像

ch2

解决方法

所以我要说的第一个反应是“只要让他们同步运行,就会容易得多。”

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各自消耗自己的速率。如果您希望它们彼此分开,则必须使用临时存储来解决。 easy 的出路是只给通道一些缓冲空间:

ch1 = make(chan int,10)
ch2 = make(chan int,10)

现在,ch1可以运行得更快-但它只能比ch2快10个项目。反之亦然。

如果要使用大小不受限制的缓冲区,则必须自己保留。我们可以利用以下事实:nil通道完全可以用作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
}

有点复杂-我不完全确定这是正确的。另外请注意,我们并没有为存储流中的旧项目释放存储空间。

,

如果来自ch1ch2的值以不同的速率消耗,并且您希望分配给两者而不必等待另一个,则sendToBoth()必须缓冲值,直到它们被接收。在您的示例中,此缓冲区将不断增长。您要如何处理?

实现此缓冲的一种非常简单合理的方法是创建,使用和返回缓冲的通道:

func sendToBoth(ch chan int) (ch1 chan int,ch2 chan int) {
    ch1 = make(chan int,100)
    ch2 = make(chan int,100)
    go func() {
        for n := range ch {
            ch1 <- n
            ch2 <- n
        }
    }()
    return ch1,ch2
}

仅此而已。只要它们的缓冲区未满,在ch1ch2上的发送将不会阻塞。当然,如果ch1ch2的缓冲区之一已满,则进一步的发送将不得不等待,直到收到来自它的元素,但这必须是可以接受的折衷方案,否则对缓冲元素的增长没有限制。您可以使用任何希望的缓冲区大小。

进行此更改后,输出将如您所愿:

[1],(1),[2],(2),[3],[4],(3),[5],[6],(4),[7],[8],(5),[9],[10],(6),[11],[12],(7),[13],[14],(8),[15],[16],(9),[17],[18],

查看相关问题:How to broadcast message using channel