`waitgroup.Wait()` 导致死锁问题。

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

waitgroup.Wait() causing dead lock

问题

我正在尝试弄清楚为什么在使用waitgroup.Wait()时会出现死锁的情况。

当我像这样运行代码时,它会打印出fatal error: all goroutines are asleep - deadlock!

我尝试将ch更改为带缓冲的通道,问题得到解决。但我真的想知道为什么会出现死锁。

英文:

I'm trying to figure out why I have a dead lock with waitgroup.Wait()

package main

import (
	"fmt"
	"sync"
)

var wg sync.WaitGroup

func foo(c chan int, i int) {
	defer wg.Done()
	c <- i
}

func main() {
	ch := make(chan int)
	for i := 0; i < 10; i++ {
		wg.Add(1)
		go foo(ch, i)
	}
	wg.Wait()
	close(ch)
	for item := range ch {
		fmt.Println(item)
	}
}

When I run it like this, it prints fatal error: all goroutines are asleep - deadlock!

I tried to change ch to a buffered channel and that solved the problem. But I really want to know why is there a dead lock.

答案1

得分: 2

我已经注释掉了你程序逻辑不正确的部分:

package main

import (
	"fmt"
	"sync"
)

var wg sync.WaitGroup

func foo(c chan int, i int) {
	defer wg.Done()
	c <- i
}

func main() {
	ch := make(chan int) // 无缓冲通道

	for i := 0; i < 10; i++ {
		wg.Add(1)
		go foo(ch, i)
	}

	// wg.Wait 等待所有 goroutine 完成,但只有在通道发送成功的情况下才可能完成。
	// 在这种情况下,由于你的接收者 "for item := range ch" 在下面,所以不可能完成,导致死锁。
	wg.Wait()

	// 理想情况下,关闭通道应该是发送者的责任。
	// 并且在接收者之前关闭一个无缓冲通道是不正确的。
	close(ch)

	for item := range ch {
		fmt.Println(item)
	}
}

修正后的程序:

package main

import (
	"fmt"
	"sync"
)

var wg sync.WaitGroup

func foo(c chan int, i int) {
	defer wg.Done()
	c <- i
}

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

	go func() {
		for item := range ch {
			fmt.Println(item)
		}
	}()

	for i := 0; i < 10; i++ {
		wg.Add(1)
		go foo(ch, i)
	}

	wg.Wait()
	close(ch)
}
英文:

I've commented out the parts where your program's logic is not correct:

package main

import (
	&quot;fmt&quot;
	&quot;sync&quot;
)

var wg sync.WaitGroup

func foo(c chan int, i int) {
	defer wg.Done()
	c &lt;- i
}

func main() {
	ch := make(chan int) // unbuffered channel

	for i := 0; i &lt; 10; i++ {
		wg.Add(1)
		go foo(ch, i)
	}

	// wg.Wait is waiting for all goroutines to finish but that&#39;s
	// only possible if the send to channel succeeds. In this case,
	// it is not possible as your receiver &quot;for item := range ch&quot; is below
	// this. Hence, a deadlock.
	wg.Wait()

	// Ideally, it should be the sender&#39;s duty to close the channel.
	// And closing a channel before the receiver where the channel
	// is unbuffered is not correct.
	close(ch)

	for item := range ch {
		fmt.Println(item)
	}
}

Corrected program:

package main

import (
	&quot;fmt&quot;
	&quot;sync&quot;
)

var wg sync.WaitGroup

func foo(c chan int, i int) {
	defer wg.Done()
	c &lt;- i
}

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

	go func() {
		for item := range ch {
			fmt.Println(item)
		}
	}()

	for i := 0; i &lt; 10; i++ {
		wg.Add(1)
		go foo(ch, i)
	}

	wg.Wait()
	close(ch)
}

huangapple
  • 本文由 发表于 2021年7月19日 13:08:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/68435353.html
匿名

发表评论

匿名网友

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

确定