在非缓冲通道上发生死锁

huangapple go评论148阅读模式
英文:

deadlock on not buffered channel

问题

第一个版本的代码产生死锁的根本原因是在没有接收者的情况下向通道发送数据。在这种情况下,发送操作会阻塞,直到有接收者准备好接收数据。由于没有接收操作,发送操作无法完成,导致死锁。

在第一个版本的代码中,创建了一个无缓冲的通道c := make(chan string),然后立即执行了发送操作c <- "test"。由于没有接收操作,发送操作无法完成,程序无法继续执行,最终导致死锁。

修复这个问题的方法有两种:

  1. 使用带缓冲的通道:c := make(chan string, 1)。带缓冲的通道可以在没有接收者的情况下缓存一定数量的数据,发送操作只有在通道已满时才会阻塞。
  2. 在不同的线程中执行发送操作:go func() { c <- "test" }()。通过在一个新的线程中执行发送操作,可以避免发送操作阻塞主线程,从而解决死锁问题。
英文:

I'm currently following the tour of go tutorial and got to the section on channels, as I was doing some testing I found an weird behavior I'm struggling to understand

the following code produces a deadlock error

package main

import &quot;fmt&quot;

func main() {
    c := make(chan string)
    c &lt;- &quot;test&quot;
    fmt.Printf(&quot;%v&quot;, &lt;- c)
}

but doing one of the following things fixes the code

using a buffered channel:

package main

import &quot;fmt&quot;

func main() {
    c := make(chan string, 1)
    c &lt;- &quot;test&quot;
    fmt.Printf(&quot;%v&quot;, &lt;- c)
}

or setting the value to the channel on a different thread

package main

import &quot;fmt&quot;

func main() {
    c := make(chan string)
    go func(){c &lt;- &quot;test&quot;}()
    fmt.Printf(&quot;%v&quot;, &lt;- c)
}

what is the underlying reason for the first version of the code to produce a deadlock?

答案1

得分: 7

写入一个非缓冲通道只有在有另一个 goroutine 从该通道读取时才会成功。在第一种情况下,你只有一个 goroutine,即主 goroutine,它向一个非缓冲通道写入,而没有其他的 goroutine 可以从中读取,所以会发生死锁。

第二种情况可以工作,因为通道是有缓冲的,通过填充缓冲区来成功写入。如果没有读取操作的情况下进行第二次写入将会发生死锁。

第三种情况可以工作,因为写入操作发生在一个单独的 goroutine 中,它会等待第一个 goroutine 中的读取操作运行。

英文:

Writing to an unbuffered channel will only succeed if there is another goroutine that reads from that channel. In the first case, you have only one goroutine, the main goroutine, that writes to an unbuffered channel, and there is no other goroutine that can read from it, so it is a deadlock.

The second one works, because the channel is buffered, and writing succeeds by filling the buffer. A second write without a read will deadlock.

The third one works, because write happens in a separate goroutine, which waits until the read in the first goroutine runs.

答案2

得分: 4

由于通道没有缓冲区,c <- "test" 将会阻塞,直到有其他地方从 c 中读取数据。由于读取操作在写入操作之后,它永远无法达到读取操作,从而导致死锁。

如果通道有一个缓冲区,c <- "test" 将会将数据写入缓冲区,而不需要等待读取操作。然后读取操作可以从通道缓冲区中读取数据。

这是因为读取操作和写入操作在同一个 goroutine 中,所以它们必须按顺序执行。如果读取操作和写入操作在不同的 goroutine 中,写入操作的 goroutine 可以阻塞,直到读取操作的 goroutine 读取数据。因此,缓冲区通常是不必要的。

英文:

Since the channel has no buffer, c &lt;- &quot;test&quot; will block until something reads from c. Since the reader comes after the write it will never reach the read and deadlock.

If the channel has a buffer, c &lt;- &quot;test&quot; writes to the buffer and does not have to wait for a reader. The reader then reads from the channel buffer.

This is all because the reader and writer are in the same goroutine and so must execute one statement after the other. If the reader and writer were in different goroutines, the writer goroutine can block until the reader goroutine reads. Because of this, buffers are often unnecessary.

huangapple
  • 本文由 发表于 2021年5月27日 05:33:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/67712990.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定