【问题标题】:Write to channel blocked forever写入永久阻塞的通道
【发布时间】:2017-06-06 12:48:06
【问题描述】:

我陷入了一种奇怪的情况,对通道的写操作永远不会发生。

package main

import (
    "fmt"
    "time"
)

func main() {
    c := make(chan int)
    s := make(chan bool)
    k := make(chan bool)
    fmt.Println("start")
    go func() {
    fmt.Println("routine 1")
        s <- true
    }()
    go func() {
    fmt.Println("routine 2")
        for {
            select {
            case  <-s :
                for i := 0; i < 3; i++ {
                    fmt.Println("before")
                    c <- i
                    fmt.Println("after")
                }       
            case <- k :
                fmt.Println("k ready")
                break
            }
        }
    }()

    go func() {
        fmt.Println("routine 3")
        for {
            select {
                case x := <- c :
                fmt.Println("x=", x)
                k <- true
                fmt.Println("k done")

            }
        }
    }()

    time.Sleep(1000 * time.Millisecond)
}

这是输出:

start
routine 1
routine 2
before
routine 3
x= 0
after
before

我想知道为什么写入通道 k 会阻塞,但日志语句 fmt.Println("k ready") 从未打印出来。

这是我的想法:

  • goroutine 1 将 true 写入通道 s
  • 执行例程 2 将 0 写入 通道 c 并等待,因为缓冲区大小为 0,它将无法 除非有人阅读频道 c,否则向其写入 '1'
  • goroutine 3 进入图片,读取通道 c(现在 goroutine 2 可以在执行例程 2 恢复后写入 c)打印 x 的值。 现在它应该可以写入通道 K 了,但这并没有发生

据我所知,它应该能够写入通道 k,然后 goroutine 的案例 2 应该执行并打印“k ready”

谁能解释我为什么写信给被屏蔽的频道? 作为修复,我知道我可以增加通道 c 的缓冲区大小,所有内容都会被打印出来,但我对修复这个问题不感兴趣,而是想了解这种情况。

一个很好的blog来理解上面的案例。

【问题讨论】:

  • 以上代码的链接是here

标签: go deadlock channel goroutine


【解决方案1】:

你有一个死锁。

  • goroutine 1 写入 s 然后退出
  • goroutine 2 从s 读取,并写入c
  • goroutine 3 从c 读取,并写入k,这会阻塞,因为没有从k 读取,因为 goroutine 2 在上面写入k 时被阻塞。
  • goroutine 2 再次写入 c,这会阻塞,因为 goroutine 3 仍在尝试写入 k,因此未从 c 读取

与您所说的相反,您没有 1 的缓冲区大小。您的缓冲区大小为零(即无缓冲通道),因此写入将阻塞,直到有内容读取。这可能是你误解的根源。根据language specification

可以使用内置函数make 生成新的初始化通道值,该函数将通道类型和可选容量作为参数:

make(chan int, 100)

容量(元素数量)设置通道中缓冲区的大小。如果容量为零或不存在,则通道是无缓冲的,并且只有在发送方和接收方都准备好时通信才会成功。否则,如果缓冲区未满(发送)或非空(接收),则通道被缓冲并且通信成功而不会阻塞。 nil 通道永远无法进行通信。

【讨论】:

  • 感谢@abligh 的回复,但似乎有一些错字或我误解了你的答案。 Point#2:写入通道 k 发生在 goroutine 3 中 goroutine 3 永远不会按照你的方式执行?
  • 并理解您提到的缓冲区大小概念,但我错误地说大小为 1 而不是 0,我读到 here。感谢您纠正我。
  • @sheikhsabeer 我已经修复了死锁解释
  • @sheikhsabeer 在那个链接中我看不到任何建议 make 在没有参数的通道上创建一个缓冲区大小为 1 的缓冲通道。make 没有参数会创建一个无缓冲通道。 golang-book.com/books/intro/10 的底部解释了差异,golang.org/ref/spec#Channel_types 更正式地解释了 - 我将编辑答案以包括这一点。
  • 再次感谢回复对更新解释的几个问题:Point#3:如果 goroutine 3 写入通道,则输出应该有日志 k done 并且 goroutine 2 仅读取通道 k不给它写信。
猜你喜欢
  • 2015-05-31
  • 2016-09-23
  • 1970-01-01
  • 2018-08-03
  • 2011-12-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多