与make channel相关的奇怪死锁问题

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

Wierd deadlock related with make channel

问题

我遇到了Go语言中通道的奇怪行为。问题描述如下:

package main

import "fmt"

func main() {
    ch := make(chan int)

    fmt.Println("len:", len(ch))
    fmt.Println("cap:", cap(ch))
    fmt.Println("is nil:", ch == nil)

    go func(ch chan int){
        ch <- 233
    }(ch)

    fmt.Println(<- ch)
}

当我运行上述代码时,得到的结果如下:

len: 0
cap: 0
is nil: false
233

通道ch的长度(len)和容量(cap)看起来很奇怪,但代码仍然可以正常工作。但是当我运行以下代码时:

package main

import "fmt"

func main() {
    ch := make(chan int)

    fmt.Println("len:", len(ch))
    fmt.Println("cap:", cap(ch))
    fmt.Println("is nil:", ch == nil)

    ch <- 233 // 这里有改动!

    fmt.Println(<- ch)
}

结果变成了:

len: 0
cap: 0
is nil: false
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main()
    /tmp/sandbox640280398/main.go:12 +0x340

而且,当我将第二段代码修改如下:

package main

import "fmt"

func main() {
    ch := make(chan int, 1) // 这里有改动!

    fmt.Println("len:", len(ch))
    fmt.Println("cap:", cap(ch))
    fmt.Println("is nil:", ch == nil)

    ch <- 233

    fmt.Println(<- ch)
}

事情又恢复正常了,我得到了:

len: 0
cap: 1
is nil: false
233

那么,有人能回答以下问题吗:

  1. 为什么make(chan int)返回一个长度为、容量为的通道,但在第一段代码中仍然可以正常工作?

  2. 为什么第二段代码在主函数中使用通道而不是新的goroutine会导致死锁?

  3. 为什么在第三段代码中给make添加一个容量参数可以解决问题?

  4. 第一段和第二段代码中的通道与空通道之间有什么区别?

英文:

I met the strange behavior of Go's channel. The question is described as the following.

package main

import &quot;fmt&quot;

func main() {
    ch := make(chan int)

    fmt.Println(&quot;len:&quot;, len(ch))
    fmt.Println(&quot;cap:&quot;, cap(ch))
    fmt.Println(&quot;is nil:&quot;, ch == nil)
                                                                         
    go func(ch chan int){
	    ch &lt;- 233
    }(ch)

    fmt.Println(&lt;- ch)

}

When I run the code above, I got result like this:

len: 0
cap: 0
is nil: false
233

The len and cap of the channel ch seem wierd but the code still works. But when I run this code:

package main

import &quot;fmt&quot;

func main() {
    ch := make(chan int)

    fmt.Println(&quot;len:&quot;, len(ch))
    fmt.Println(&quot;cap:&quot;, cap(ch))
    fmt.Println(&quot;is nil:&quot;, ch == nil)
                                                                         
    ch &lt;- 233 // Here changes!

	fmt.Println(&lt;- ch)

}

The result became:
len: 0
cap: 0
is nil: false
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main()
    /tmp/sandbox640280398/main.go:12 +0x340

What's more, when I change the second code piece like the following:
package main

import &quot;fmt&quot;

func main() {
    ch := make(chan int, 1) //Here changes!

    fmt.Println(&quot;len:&quot;, len(ch))
    fmt.Println(&quot;cap:&quot;, cap(ch))
    fmt.Println(&quot;is nil:&quot;, ch == nil)
                                                                         
	ch &lt;- 233

	fmt.Println(&lt;- ch)

}

Things worked again, I got:

len: 0
cap: 1
is nil: false
233

So, can anybody tell me the following questions:

  1. Why make(chan int) return a channel with zero len and zero cap but still can work well in first code piece?

  2. Why the second code use the channel in the main function instead of a new goroutine cause the deadlock?

  3. Why I add a cap parameter to make in third code can fix the problem?

  4. What's the difference between the channel(in 1st and 2nd code) with the nil channel?

答案1

得分: 4

你可以创建两种类型的通道:有缓冲通道和无缓冲通道。

有缓冲通道是指具有容量的通道:make(chan int, 10)

有缓冲通道允许你发送与其容量相同数量的消息而不被阻塞。

无缓冲通道没有容量,这就是为什么你的发送goroutine会被阻塞,直到另一个goroutine从中接收到消息。

  1. 这是一个无缓冲通道。你的主goroutine在接收通道消息时被阻塞,直到有新的goroutine向其发送消息。

  2. 因为你使用的是无缓冲通道,你的发送goroutine会被阻塞,直到另一个goroutine接收到消息。但是除了主goroutine之外,你没有其他的goroutine,所以程序陷入了死锁状态。

  3. 这是因为有缓冲的goroutine。它的容量为1,所以发送一条消息并在同一个goroutine中接收它不会有问题。但是如果你尝试发送多于1条消息,你将被阻塞。ch <- 233; ch <- 233 - 这段代码会导致死锁。

  4. 我理解你的意思了,但是如果你尝试从一个空通道接收或发送消息,你将被阻塞:var ch chan int; <-chvar ch chan int; ch <- 1

英文:

You can create two types of channels: buffered channels and unbuffered channels.
Buffered channel are those that has capacity: make(chan int, 10)
Buffered channels allow you to send into them same amount of messages as their capacity without being blocked.
Unbuffered channels has no capacity and that is why your sending goroutine will be blocked until another goroutine receive from it.

1. It is unbuffered channel. You main goroutine is blocked on receiving from channel until new goroutine send message to it.

2. Because you are using unbuffered channel your sending goroutine is blocked until another receive from it, but you have no other goroutines except that main one, so the program is in deadlock.

3. Because of buffered goroutine. It has capacity of 1, so it is won't be problem to send one message to it and then receive it in the same goroutine. But you will be blocked if you try to send more than 1 message to it. ch &lt;- 233; ch &lt;- 233 - this code will cause a deadlock.

4. Did get what you mean..., but if you try to receive or send to a nil channel you will be blocked: var ch chan int; &lt;-ch or var ch chan int; ch &lt;- 1

huangapple
  • 本文由 发表于 2017年7月28日 15:05:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/45366954.html
匿名

发表评论

匿名网友

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

确定