make(chan bool) 与 make(chan bool, 1) 的行为有何不同?

Posted

技术标签:

【中文标题】make(chan bool) 与 make(chan bool, 1) 的行为有何不同?【英文标题】:How does make(chan bool) behave differently from make(chan bool, 1)? 【发布时间】:2013-12-01 05:25:07 【问题描述】:

我的问题来自于尝试读取频道(如果可以),或者尝试使用select 语句写入频道。

我知道像make(chan bool, 1) 这样指定的频道被缓冲了,我的部分问题是这之间有什么区别,而make(chan bool)——this page 说的和make(chan bool, 0) 一样——可以在其中容纳 0 值的通道有什么意义?

playground A

chanFoo := make(chan bool)

for i := 0; i < 5; i++ 
    select 
    case <-chanFoo:
        fmt.Println("Read")
    case chanFoo <- true:
        fmt.Println("Write")
    default:
        fmt.Println("Neither")
    

输出:

Neither
Neither
Neither
Neither
Neither

(删除default 会导致死锁!!)

现在见playground B

chanFoo := make(chan bool, 1)   // the only difference is the buffer size of 1

for i := 0; i < 5; i++ 
    select 
    case <-chanFoo:
        fmt.Println("Read")
    case chanFoo <- true:
        fmt.Println("Write")
    default:
        fmt.Println("Neither")
    

B 输出:

Write
Read
Write
Read
Write

就我而言,B 输出 是我想要的。无缓冲通道有什么好处?我在 golang.org 上看到的所有示例似乎都使用它们一次发送一个信号/值(这就是我所需要的)——但就像在游乐场 A 中一样,通道永远不会被读取 or书面。在我对渠道​​的理解中,我在这里遗漏了什么?

【问题讨论】:

"什么是通道可以容纳0值"这里的第二个参数表示缓冲区大小,所以这只是一个没有缓冲区的通道(无缓冲通道) 如果 Read 和 Write 在不同的 goroutines 中,示例 A 可以工作。默认通道,没有容量,是无缓冲的:发送一些东西会阻塞发送方,直到接收方从通道中读取,所以你需要将两者放在不同的 goroutines 中。 @siritinga 如果您将其扩展为答案,我想就是这样。因此,非缓冲通道会阻塞,除非有人已经在等待它,而大小为 1 的缓冲通道将保持值直到有人准备好接收它。 参见:Why using unbuffered channel in the the same goroutine gives a deadlock 【参考方案1】:

无缓冲的通道(无容量创建)会阻塞发送方,直到有人可以从中读取,因此为了使其按预期工作,您应该使用两个 goroutine 来避免同一线程中的死锁。

例如,使用此代码:http://play.golang.org/p/KWJ1gbdSqf

它还包括一个机制,让 main func 检测两个 goroutine 何时完成。

package main

import "fmt"

func send(out, finish chan bool) 
    for i := 0; i < 5; i++ 
        out <- true
        fmt.Println("Write")
    
    finish <- true
    close(out)


func recv(in, finish chan bool) 

    for _ = range in 
        fmt.Println("Read")
    
    finish <- true



func main() 
    chanFoo := make(chan bool)
    chanfinish := make(chan bool)

    go send(chanFoo, chanfinish)
    go recv(chanFoo, chanfinish)

    <-chanfinish
    <-chanfinish

它不会显示交替的读取和写入,因为一旦send 写入通道,它就被阻塞,在它可以打印“写入”之前,所以执行转移到接收到的recv通道并打印“读取”。它将尝试再次读取通道,但会被阻止并且执行移动到send。现在send 可以写第一个“Write”,发送到通道(没有阻塞,因为现在有一个接收器准备好)并写第二个“Write”。在任何情况下,这都不是确定性的,调度程序可以随时将执行转移到任何其他正在运行的 goroutine(至少在最新的 1.2 版本中)。

【讨论】:

【参考方案2】:

无缓冲通道意味着从通道读取和写入通道是阻塞的。

select 声明中:

如果某个其他 goroutine 当前在写入通道时被阻止,则读取将起作用 如果其他一些 goroutine 当前在读取通道时被阻止,则写入将起作用 否则会执行 default 案例,这发生在您的案例 A 中。

【讨论】:

【参考方案3】:

可以在其中容纳 0 值的通道的意义是什么

首先我要指出,这里的第二个参数表示缓冲区大小,所以它只是一个没有缓冲区的通道(无缓冲通道)。

其实这就是你的问题产生的原因。无缓冲通道仅在有人阻止读取时才可写,这意味着您将有一些协程可以使用——而不是单个协程。

另见The Go Memory Model:

来自无缓冲通道的接收发生在该通道上的发送完成之前。

【讨论】:

Bingo -- 我还没有完全了解内存模型文档。这样就搞清楚了。谢谢!

以上是关于make(chan bool) 与 make(chan bool, 1) 的行为有何不同?的主要内容,如果未能解决你的问题,请参考以下文章

c:=make(chan int) 和 c:=make(chan int,1) 有啥区别?

`var a chan int` 和 `a := make(chan int)` 有啥区别?

我可以在 go 中使用 make(chan someStruct) 吗?

Go语言中new()和make()的区别

Go管道注意细节

go 锁 速度 chan 和 mutex的比较