重写通道,同时监听它并进行循环处理

Rewrite channel while listening on it and for loop handling

在频道中尝试一些实验时,我想到了以下代码:

var strChannel = make(chan string, 30)
var mutex = &sync.Mutex{}

func main() {

    go sampleRoutine()

    for i := 0; i < 10; i++ {
        mutex.Lock()
        strChannel <- strconv.FormatInt(int64(i), 10)
        mutex.Unlock()
        time.Sleep(1 * time.Second)
    }

    time.Sleep(10 * time.Second)
}

func sampleRoutine() {
/*  A: for msg := range strChannel{*/
/*  B: for {
        msg := <-strChannel*/
        log.Println("got message ", msg, strChannel)
        if msg == "3" {
            mutex.Lock()
            strChannel = make(chan string, 20)
            mutex.Unlock()
        }
    }
}

基本上在收听给定频道时,我将频道变量分配给特定条件下的新频道(此处为 msg == 3)。

当我使用注释块 B 中的代码时,它按预期工作,即循环移动到新创建的频道并打印 4-10。

但是我认为只是另一种编写循环的注释块 A 不起作用,即在打印“3”后它停止了。

有人可以告诉我这种行为的原因吗?

还有像这样的代码,其中一个例程监听一个频道,创建一个新的安全吗?

  1. 从通道发送和读取 不需要 需要互斥锁保护:它们由自己[=]充当同步原语25=](这是 sending/receiving 背后的主要思想之一)在频道上。

  2. 变体A和B没有区别,因为你没有关闭通道。但是...

  3. 在迭代旧频道的同时将新频道重新分配给 strChannel 是错误的。不要那样做。甚至没有 B 变体。将 range strChannel 视为 "please range over the values in the channel currently stored in the variable strChannel"。这个范围将 "continue on the original channel" 并在同一个变量中存储一个新频道不会改变这一点。避免这样的代码!

在 Go 中,for 语句在循环开始之前计算 range 右侧的值。

也就是说改变range右边的变量值不会有任何效果。因此,在您的代码中,在变体 A 中,msg 不断迭代原始通道并且从未改变。在 Varaint B 中,它按预期工作,因为每次迭代都会评估通道。

这个概念有点棘手。这并不意味着您不能修改 range 右侧 slicemap。如果深入研究,你会发现在Go中,mapslice存储的是一个指针,修改它的item并不会改变那个指针,所以是有效果的。

array 的情况下更加棘手。修改 range 右侧的 arrayitem 没有任何效果。这是由于 Go 将数组存储为值的机制。

游乐场示例:https://play.golang.org/p/wzPfGHFYrnv