迭代时替换通道

replace channel while iterating

我想在某些情况下用新频道替换频道,例如:

package main

import (
    "log"
    "time"
)

func subMsg(s string) chan string {
    ch := make(chan string)
    go func() {
        ticker := time.NewTicker(time.Second * 2)
        for range ticker.C {
            ch <- s
        }
    }()

    return ch
}

func main() {
    chStr := subMsg("hello")

    go func() {
        i := 0
        for s := range chStr {
            log.Print(s)
            i++
            if i > 5 {
                log.Print("new topic")
                i = 0

                chStr = subMsg("world")
            }
        }
    }()

    select {}
}

我希望此代码片段输出 5 "hello",然后输出 "world",但它并没有这样工作。我不太清楚重新分配频道时发生了什么。有什么建议吗?

您正在使用 for range,并且根据 Spec: For statements 范围表达式仅计算一次。

The range expression x is evaluated once before beginning the loop, with one exception: if at most one iteration variable is present and len(x) is constant, the range expression is not evaluated.

稍后 for 循环未检查 chStr 变量,因此更改其值无效。

如果你想切换到另一个频道,你不能使用for range

只需使用 "normal" 循环并从其中的频道接收。使用特殊形式 x, ok := <-ch 以便您知道通道何时关闭,这样您就可以跳出循环(模仿 for range 的工作方式):

for {
    s, ok := <-chStr
    if !ok {
        break
    }
    log.Print(s)
    i++
    if i > 5 {
        log.Print("new topic")
        i = 0

        chStr = subMsg("world")
    }
}

Go Playground 上试用。