Go中缓冲通道和非缓冲通道的测距有啥区别?

Posted

技术标签:

【中文标题】Go中缓冲通道和非缓冲通道的测距有啥区别?【英文标题】:What are the differences between ranging over a buffered channel and non-buffered channel in Go?Go中缓冲通道和非缓冲通道的测距有什么区别? 【发布时间】:2018-04-15 15:50:30 【问题描述】:

我正在尝试类似于以下模式的东西:

func sendFunc(n int, c chan int) 
    for i := 0; i < n; i++ 
        c <- i
        fmt.Println("Pushed")
    
    close(c)


func main() 
    c := make(chan int, 10)
    go sendFunc(10, c)
    // Receive from the channel
    for i := range c 
        fmt.Println(i)
    

输出似乎是同步的,如下所示:

Pushed
Pushed
Pushed
Pushed
Pushed
Pushed
Pushed
Pushed
Pushed
Pushed
0
1
2
3
4
5
6
7
8
9

如果我将缓冲通道更改为非缓冲通道:

c := make(chan int)

结果似乎是异步的:

Pushed
0
1
Pushed
Pushed
2
3
Pushed
Pushed
4
5
Pushed
Pushed
6
7
Pushed
Pushed
8
9
Pushed

为什么它的行为不同?

更新

所以我的场景是:在接收器中,每次从生产者接收到新数据时都会发出一个请求,结果表明调度程序直到所有数据都发送到通道后才开始接收(给定一个缓冲的有足够空间的频道),除非生产者被暂停(例如通过调用time.sleep())。因此我最终使用了非缓冲通道,以便等待响应的时间和生产者中处理数据的时间可以重叠,从而提高并发性。

【问题讨论】:

你正在观察 goroutine 调度器的效果。在发送循环中添加 time.Sleep(time.Second) 并观察会发生什么。 我建议您运行 go trace 工具并可视化跟踪。它将帮助您了解两个 go 例程是如何运行的。有关如何使用跟踪工具***.com/questions/32131339/the-go-1-5-trace-command,请参阅此答案 【参考方案1】:

正如 Cerise Limón 已经说过的:这是运行时计划如何执行例程的影响。基本上,只要不阻塞或返回,go 例程就会运行。因此调用go sendFunc(10, c) 将一直执行,直到它阻塞或返回。如果你在sendFunc 中加入&lt;-time.After(1),函数会突然阻塞,你会得到调度器将调度另一个例程的效果。

这是操场上的一个小例子: https://play.golang.org/p/99vJniOf3_

哪个更好的问题很难回答。免责声明:到目前为止,我还不是这方面的专家,但我想这是一种权衡。虽然较小的缓冲区减少了单个消息在缓冲区中停留的时间,但它会触发 go 例程的重新调度,这通常会花费一些时间。

另一方面,较大的缓冲区会增加通过缓冲区的消息延迟,但另一方面会提高吞吐量。此外,您还可以预先生成大量消息,如果您有一些对于一条或多条消息相同的静态开销(例如,请求单行输入与请求多行输入),这将很有用。

有这个调度器的解释https://rakyll.org/scheduler/。

【讨论】:

以上是关于Go中缓冲通道和非缓冲通道的测距有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章

每天一个知识点:Go 语言当中 Channel(通道)有啥特点,需要注意啥?

从缓冲通道和非缓冲通道中进行选择

Go非缓冲/缓冲/双向/单向通道

Go中缓冲通道的死锁

go-runtime 不会将对象推出缓冲通道

Go:缓冲通道总和更快?