【问题标题】:Reading from channels从频道读取
【发布时间】:2021-05-17 11:44:20
【问题描述】:

从 Go 中的通道读取时,我观察到它没有遵循发布功能和消费功能之间的完美同步。奇怪的是,如果这是 CPU 调度的一个怪癖,有时我会得到不同的结果。 main 中的消费者似乎一次消费了两个并打印出来。

考虑以下示例:

package main

import (
    "fmt"
)

func squares(ch chan int) {
    for i:=0; i<9; i++ {
        val:= i*i;
        fmt.Printf("created val %v \n", val);
        ch <- i*i;
        fmt.Printf("After posting val %v \n", val);
    }
    
    close(ch)
}

func main() {
    c := make(chan int)
    go squares(c)
    
    for val := range c{
        fmt.Println(val);
    }
}

无论我在 Go Playground 上运行多少次,我都会看到以下输出。

created val 0 
After posting val 0 
created val 1 
0
1
After posting val 1 
created val 4 
After posting val 4 
created val 9 
4
9
After posting val 9 
created val 16 
After posting val 16 
created val 25 
16
25
After posting val 25 
created val 36 
After posting val 36 
created val 49 
36
49
After posting val 49 
created val 64 
After posting val 64 
64

我不应该期待吗,因为 go 会阻止 squares 方法,直到 main 打印它?

created val 0 
0
After posting val 0 
...

如果不是,那为什么?如果我想要像上面这样的完美同步,我应该怎么做?

【问题讨论】:

  • 你有什么问题?
  • 这个输出非常好。
  • 同步发生在实际的通道操作上。打印语句不同步,它们仅在各自的 go 例程中的操作序列中排序。
  • 如果我想要完美同步怎么办?

标签: go concurrency


【解决方案1】:

您使用的是无缓冲通道,所以会发生这种情况。

注意:这并不是技术上 100% 准确的描述。

  1. 主程序启动。它一直运行到for val := range c {。然后将其置于“睡眠”状态,因为没有可从 c 读取的值。
  2. 刚刚创建的squares 的goroutine 正在“唤醒”。它创建一个值并可以在另一个 goroutine 已经“等待”通道上的值时发布它。然后它会创建另一个值并且无法发布它,因为通道现在被阻塞了。
  3. 主程序被“唤醒”,读取值,打印它,读取另一个 goroutine 已经等待推送的下一个值。打印它然后再次卡住,因为没有可用的新值。
  4. sqares 的 goroutine 被“唤醒”,因为它能够推送它的值。它打印“发布后”,创建一个新值,将其发布为另一个例程已经在等待一个,然后创建另一个。然后它卡住了,因为另一个例程还没有准备好接收下一个值。
  5. 返回 3)

如果您想要一个“更顺畅”的工作流程,其中例程不会同步通过通道传递的每个值,请创建一个缓冲通道:

c := make(chan int, 3)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-01-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-26
    • 2021-01-21
    相关资源
    最近更新 更多