使用两个 fmt.Println 和一个 go 协程时出现死锁问题?

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

Deadlock when using two fmt.printlns with a go routine?

问题

我正在尝试学习Go语言,并在Playground中进行实验。我有一个非常简单的Go代码。我尝试在Go协程中同时使用结构体和切片。我不确定这是否会在实际生产中使用,但它似乎有点不对劲,所以在这里是代码:

func main() {
	routinemsg := make(chan []Person)
	routinemsg2 := make(chan []Person)

	// 创建Person记录
	p1 := newPerson("john doe", 25)
	p2 := newPerson("dohn joe", 52)
	p3 := newPerson("bohn joo", 30)

	// 将一个Person切片发送到第一个协程
	go func() { routinemsg <- []Person{p1, p2} }()

	// 从第一个协程中检索切片[在append中]
	// 将p3追加到从第一个协程中检索到的切片中
	// 将新的切片发送到第二个协程
	go func() { routinemsg2 <- append(<-routinemsg, p3) }()

	// 我能看到第一个Println,但当我插入第二个Println时,我会得到一个死锁错误
	// 如果我使用一个Println和两个参数,也会出现相同的错误
	fmt.Println(<-routinemsg)
	fmt.Println(<-routinemsg2)
}

我听说过等待组,但还不了解它们!所以,请对我友好一点:D,谢谢你的时间。

英文:

I am trying to learn Go and I was experimenting in the playground. I have a very simple go code. I was trying to use Structs and Slices together in a go routine. I am not sure if this will be a thing I will use in production but it seemed a little off, so here:


func main() {
	routinemsg := make(chan []Person)
	routinemsg2 := make(chan []Person)

    // create the person records
	p1 := newPerson(&quot;john doe&quot;, 25)
	p2 := newPerson(&quot;dohn joe&quot;, 52)
	p3 := newPerson(&quot;bohn joo&quot;, 30)

    // send a slice of Person to the first routine
	go func() { routinemsg &lt;- []Person{p1, p2} }()

    // retrieve the slice from the first routine[in the append]
    // append p3 to the slice retrieved from the first routine
    // send the new slice to the second routine
	go func() { routinemsg2 &lt;- append(&lt;-routinemsg, p3) }()
    
    // I am able to see the first Println but when I insert the second one I get a deadlock error
    // also, same error if I use one Println with 2 arguments.
	fmt.Println(&lt;-routinemsg)
	fmt.Println(&lt;-routinemsg2)
}

I heard about wait groups but dont know them yet! So, be nice to me 使用两个 fmt.Println 和一个 go 协程时出现死锁问题? and thanks for your time

答案1

得分: 1

routinemsg只有一个发送操作,但你从它那里有两个接收操作:一个在启动的goroutine中,另一个在main goroutine中。一个发送的值只能被一个接收者接收一次。

如果启动的goroutine先从routinemsg接收,那么会发生死锁:main中的接收操作将永远阻塞。

如果main goroutine先接收,那么启动的goroutine将永远阻塞(尝试从它接收),因此它永远无法在routinemsg2上发送任何内容,因此main中对routinemsg2的接收也将永远阻塞:再次发生死锁。

main()函数中删除fmt.Println(<-routinemsg)这一行,然后最后对routinemsg2的接收操作可以(最终)继续执行,并打印包含p1p2p3的切片:

[{john doe 25} {dohn joe 52} {bohn joo 30}]

Go Playground上尝试一下。

英文:

There is only a single send operation on routinemsg, but you have 2 receive operations from it: one in a launched goroutine and another in the main goroutine. A sent value can only be received once, by one receiver.

If the launched goroutine receives from routinemsg first, then it will be a deadlock: the receive in the main will block forever.

If the main goroutine would receive first, then the launched goroutine would block forever (trying to receive from it), hence it could never sending anything on routinemsg2, hence the receive from routinemsg2 in main would also block forever: deadlock again.

Remove the fmt.Println(&lt;-routinemsg) line in main(), and then the final receive from routinemsg2 can (eventually) proceed and will print the slice holding p1, p2 and p3:

[{john doe 25} {dohn joe 52} {bohn joo 30}]

Try it on the Go Playground.

答案2

得分: 0

作为对@icza答案的补充:如果你使用下面这样的带缓冲通道

    buffered := make(chan string, 2)
	buffered <- "1"
	buffered <- "2"

	fmt.Println(<-buffered)
	fmt.Println(<-buffered)

你可以从同一个通道发送和接收多次值。

然而,你仍然通过传递给make的参数限制了你可以发送和接收的次数!而且,使用带缓冲通道,你将始终按照它们被发送到通道的顺序接收值。

因此,你从第二个接收器接收的第二个值将始终是发送到通道的第二个值。

英文:

As a supplementary to @icza's answer: if you use a buffered channel like below,

    buffered := make(chan string, 2)
	buffered &lt;- &quot;1&quot;
	buffered &lt;- &quot;2&quot;

	fmt.Println(&lt;-buffered)
	fmt.Println(&lt;-buffered)

you can send and receive values more than once from the same channel.

HOWEVER, you still limit the times you can send&receive by the argument you pass into make! Also, with buffered channels, you will ALWAYS receive values in the order they were sent to the channel.

So, second value you receive from the second receiver will always be the second value sent to the channel.

huangapple
  • 本文由 发表于 2022年11月26日 00:31:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/74575621.html
匿名

发表评论

匿名网友

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

确定