go 语言的一个死锁问题

2021-06-03 18:47:54 +08:00
 v2defy

两个 goroutine,一个输出奇数,一个输出偶数,交替输出,最终达到顺序输出的目的。

其中 B 协程运行到最后的时候,如果不做特殊的处理,继续向 A 通道写入数据,会导致 A 通道死锁。

感到很奇怪,为什么会这样? A 通道这时候是没有数据的,为什么不让写?

package main

import (
  "fmt"
)

func main() {
  // 创建 3 个 channel,A,B 和 Exit
  A := make(chan bool)
  B := make(chan bool)
  Exit := make(chan bool)

  go func() {
    // 如果 A 通道是 true,我就执行
    for i := 1; i <= 10; i += 2 {
      if ok := <-A; ok {
        fmt.Println("A 输出", i)
        B <- true
      }
    }
  }()

  go func() {
    defer func() { Exit <- true }() // 这个协程的活干完之后,向主 goroutine 发送信号
    // 如果 B 通道是 true,我就执行
    for i := 2; i <= 10; i += 2 {
      if ok := <-B; ok {
        fmt.Println("B 输出", i)
        if i != 10 { // 如果 i 等于 10 了,就不要再向 A 通道写数据了,否则将导致 A 通道死锁,至于为什么,坦白说我很疑惑
          A <- true
        }
      }
    }
  }()

  A <- true // 启动条件
  <-Exit    // 结束条件
}
1692 次点击
所在节点    Go 编程语言
7 条回复
darrh00
2021-06-03 19:01:53 +08:00
第二个 goroutine 运行到 i = 10 的时候,第一个 goroutine 中的 for 循环已经结束退出了,因此 A 通道再也没有了接收者,而第二个 goroutine 里还继续向 A 通道写数据,当然就出错了。
SteinsGate
2021-06-03 19:03:13 +08:00
因为第一个退出了,无缓冲区通道,没人消费 A,卡在了 A <- true
v2defy
2021-06-03 19:05:35 +08:00
@darrh00 原来是这样!!感谢大佬,一句话就解答了我的疑惑。
v2defy
2021-06-03 19:06:11 +08:00
@SteinsGate 谢谢大佬,懂了
GTim
2021-06-03 19:17:33 +08:00
给你换种方式输出结果你就知道了

```go
package main

import (
"fmt"
)

func main() {
// 创建 3 个 channel,A,B 和 Exit
A := make(chan bool)
B := make(chan bool)
Exit := make(chan bool)

go func() {
// 如果 A 通道是 true,我就执行
for i := 1; i <= 10; i += 2 {
if ok := <-A; ok {
fmt.Printf("-> A(%d)", i)
B <- true
}
}
fmt.Print("===bye==")
}()

go func() {
defer func() { Exit <- true }() // 这个协程的活干完之后,向主 goroutine 发送信号
// 如果 B 通道是 true,我就执行
for i := 2; i <= 10; i += 2 {
if ok := <-B; ok {
fmt.Printf("-> B(%d)", i)
//if i != 10 { // 如果 i 等于 10 了,就不要再向 A 通道写数据了,否则将导致 A 通道死锁,至于为什么,坦白说我很疑惑
A <- true
//}
}
}
fmt.Print("===bye==")
}()

fmt.Print("|")
A <- true // 启动条件
<-Exit // 结束条件
}

```

输出结果如下

```
|-> A(1)-> B(2)-> A(3)-> B(4)-> A(5)-> B(6)-> A(7)-> B(8)-> A(9)===bye==-> B(10)fatal error: all goroutines are asleep - deadlock!
```

原因是不带缓冲区的 chan 写入时立刻会被堵塞。看看官方怎么对无缓冲区 chan 的描述就知道了
v2defy
2021-06-03 19:34:59 +08:00
@GTim 细节啊!学费了学费了^_^
george404
2021-06-04 09:31:52 +08:00
还有,我建议用 waitgroup 来同步不同的 thread. 这样看上去也比较清晰。个人建议啦。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/781192

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX