并发使用等待组

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

concurrency using wait group

问题

我是你的中文翻译助手,以下是翻译好的内容:

我刚接触Go语言,并且正在寻求一些关于并发模型的帮助。假设我想要同时进行两个HTTP调用,并等待它们都完成后再处理/合并响应数据。以下是我目前的代码:

func main() {
    var wg sync.WaitGroup
    wg.Add(2)
    c1 := make(chan string)
    c2 := make(chan string)
    go foo(c1, &wg)
    go bar(c2, &wg)
    wg.Wait()
    foo := <-c1
    bar := <-c2
    fmt.Println("foo: ", foo)
    fmt.Println("bar: ", bar)
}

func foo(c chan string, wg *sync.WaitGroup) {
    defer wg.Done()
    c <- "foo"
}

func bar(c chan string, wg *sync.WaitGroup) {
    defer wg.Done()
    c <- "bar"
}

然而,当我运行它时,它会报错fatal error: all goroutines are asleep - deadlock!(致命错误:所有goroutine都处于休眠状态 - 死锁!)。

我可以在没有WaitGroup的情况下使其正常工作,但我只是好奇为什么会出现死锁,并且最佳的解决方法是什么。

英文:

i am new to the Go language, and looking for some help for the concurrency model.
say I want to make 2 http calls concurrently, and wait for both of them to finish then process/merge the response data.
here is the code i have

func main() {
	var wg sync.WaitGroup
	wg.Add(2)
	c1 := make(chan string)
	c2 := make(chan string)
	go foo(c1, &amp;wg)
	go bar(c2, &amp;wg)
	wg.Wait()
	foo := &lt;-c1
	bar := &lt;-c2
	fmt.Println(&quot;foo: &quot;, foo)
	fmt.Println(&quot;bar: &quot;, bar)
}

func foo(c chan string, wg *sync.WaitGroup) {
	defer wg.Done()
	c &lt;- &quot;foo&quot;
}

func bar(c chan string, wg *sync.WaitGroup) {
	defer wg.Done()
	c &lt;- &quot;bar&quot;
}

however when i run it, it gives the error fatal error: all goroutines are asleep - deadlock!

I can get it working without the WaitGroup, but just curious why this gets to the deadlock, and whats the best way of doing it

答案1

得分: 2

这里的goroutine会在等待向通道写入数据时被阻塞,因为通道的读取操作发生在goroutine结束之后(在wg.Done之后),从而导致死锁。

解决方法很简单,可以去掉等待组(waitgroup)。通道的读取操作会在goroutine向通道写入数据之前被阻塞,因此在两个通道都被读取后,就不需要再等待了。

英文:

The goroutines will block waiting to write to the channel, because the channel read happens after goroutines end (after wg.Done), thus the deadlock.

Simples solution here is to get rid of the waitgroup. The channel read operations will block until the goroutines write to the channel, so after both channels are read, there is no need to wait.

答案2

得分: 1

确实,在这里并不真正需要等待组。但是,如果你想继续尝试使用 goroutine 和通道,你也可以尝试将通道设置为带缓冲的,像这样:

c1 := make(chan string, 1)
c2 := make(chan string, 1)

这样做的结果是,你可以向每个通道写入一个条目而不会阻塞。

英文:

Indeed the waitgroup is not really needed here. But if you want to continue experimenting with goroutines and channels, then you can also try and make the channels buffered, like:

c1 := make(chan string, 1)
c2 := make(chan string, 1)

What happens then is that you can write a single entry to each channel without blocking.

答案3

得分: 0

读取/写入未缓冲通道是一个阻塞调用,这意味着这些行:

c <- "foo"
c <- "bar"

将会一直阻塞,直到你调用从通道中取值的代码,也就是这些行:

foo := <-c1
bar := <-c2

造成死锁的原因是在这两行之前调用了wg.Wait()。为了继续执行wg.Wait()之后的代码,所有的等待组必须完成,但是你的等待组无法完成,因为wg.Done()被延迟到c <- "foo"/c <- "bar"解除阻塞之后。正如之前提到的,这些代码无法解除阻塞,直到你到达foo := <-c1/bar := <-c2,而由于wg.Wait()的存在,这些代码无法执行到。你可以看到没有任何进展是可能的,因此发生了死锁。

最佳实践是在绝对必要的情况下避免使用等待组和互斥锁。通常情况下(就像在这个例子中),在纯Go中可以找到解决方案,而这些包只会使代码变得更加复杂。

英文:

Reading/writing to an unbuffered channel is a blocking call, meaning that these lines:

c &lt;- &quot;foo&quot;
c &lt;- &quot;bar&quot;

will hang until you reach calls that pull the values from the channels, i.e., these lines:

foo := &lt;-c1
bar := &lt;-c2

The cause of your deadlock is calling wg.Wait() before these two lines. In order to move past wg.Wait(), all waitgroups must complete, but your waitgroups cannot because wg.Done() is deferred until c &lt;- &quot;foo&quot;/c &lt;- &quot;bar&quot; unblock. As mentioned before, these cannot unblock until you reach foo := &lt;-c1/bar := &lt;-c2, and these cannot be reached because of wg.Wait(). You see the how no progress is possible, hence the deadlock.

The best practice is to avoid using waitgroups and mutex until absolutely necessary. Often, (like in this case) a solution is possible in pure Go and these packages only complicate the code.

huangapple
  • 本文由 发表于 2021年8月10日 05:03:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/68718512.html
匿名

发表评论

匿名网友

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

确定