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

  • 本文关键字:make bool chan 何不同 go channel
  • 更新时间 :
  • 英文 :


我的问题源于尝试阅读频道(如果可以的话(或编写频道(如果可以的话(使用select语句。

我知道像make(chan bool, 1)这样指定的通道是缓冲的,我的部分问题是它和make(chan bool)有什么区别——这个页面说这与make(chan bool, 0) ---可以容纳 0 个值的通道有什么意义?

查看游乐场 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案例会导致死锁!!

现在看操场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 中一样,通道永远不会被读取写入。在我对渠道的理解中,我错过了什么?

可以容纳 0 个值的通道有什么意义

首先,我想指出这里的第二个参数表示缓冲区大小,因此这只是一个没有缓冲区的通道(无缓冲通道(。

实际上,这就是

产生问题的原因。未缓冲的通道只有在有人阻止从中读取时才是可写的,这意味着您应该使用一些协程 - 而不是这个协程。

另请参阅 Go 内存模型:

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

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

select声明中:

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

无缓冲的通道(在没有容量的情况下创建(将阻止发送方,直到有人可以从中读取,因此要使其按预期工作,您应该使用两个 goroutines 来避免同一线程中的死锁。

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

它还包括一个机制,用于主函数检测两个 goroutines 何时完成。

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可以写入第一个"写入",发送到通道(没有阻塞,因为现在有一个接收器准备好了(并写入第二个"写入"。在任何情况下,这不是确定性的,调度程序可以随时将执行移动到任何其他正在运行的goroutine(至少在最新的1.2版本中(。

最新更新