所有的goroutine都处于休眠状态 – 死锁

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

all goroutines are asleep - deadlock

问题

为了满足我的一个需求,我需要创建N个工作协程(go routines),这些协程将由一个监控协程(monitoring routine)进行监控。当所有工作协程完成时,监控协程必须结束。我的代码陷入了死锁,请帮忙看看。

import "fmt"
import "sync"
import "strconv"

func worker(wg *sync.WaitGroup, cs chan string, i int ){
    defer wg.Done()
    cs<-"worker"+strconv.Itoa(i)    
}

func monitorWorker(wg *sync.WaitGroup, cs chan string) {
    defer wg.Done()
    for i:= range cs {
        fmt.Println(i)
    }
}

func main() {
    wg := &sync.WaitGroup{}
    cs := make(chan string)

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

    wg.Add(1)
    go monitorWorker(wg,cs)
    wg.Wait()
}
英文:

For one of my requirement I have to create N number of worker go routines, which will be monitored by one monitoring routine. monitoring routine has to end when all worker routines completes. My code ending in deadlock, please help.

import &quot;fmt&quot;
import &quot;sync&quot;
import &quot;strconv&quot;

func worker(wg *sync.WaitGroup, cs chan string, i int ){
    defer wg.Done()
	cs&lt;-&quot;worker&quot;+strconv.Itoa(i)	
}

func monitorWorker(wg *sync.WaitGroup, cs chan string) {
    defer wg.Done()
	for i:= range cs {
			fmt.Println(i)
     }
}
func main() {
	wg := &amp;sync.WaitGroup{}
	cs := make(chan string)

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

    wg.Add(1)
	go monitorWorker(wg,cs)
    wg.Wait()
}

答案1

得分: 42

你的monitorWorker永远不会停止。当所有的worker完成时,它会继续等待cs。这会导致死锁,因为没有其他东西会发送到cs,因此wg永远不会达到0。一个可能的修复方法是在所有的worker完成时,让monitor关闭通道。如果for循环在main函数中,它将结束循环,从main函数返回,并结束程序。

例如:http://play.golang.org/p/nai7XtTMfr

package main

import (
	"fmt"
	"strconv"
	"sync"
)

func worker(wg *sync.WaitGroup, cs chan string, i int) {
	defer wg.Done()
	cs <- "worker" + strconv.Itoa(i)
}

func monitorWorker(wg *sync.WaitGroup, cs chan string) {
	wg.Wait()
	close(cs)
}

func main() {
	wg := &sync.WaitGroup{}
	cs := make(chan string)

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

	go monitorWorker(wg, cs)

	for i := range cs {
		fmt.Println(i)
	}
}

编辑:这是对OP第一个评论的回答。

你的程序有三个需要同步的部分。首先,所有的worker需要发送数据。然后你的打印循环需要打印这些数据。最后,你的main函数需要返回,从而结束程序。在你的示例中,所有的worker都发送了数据,所有的数据都被打印了,但是消息从未发送给main函数,告诉它应该优雅地返回。

在我的示例中,main函数负责打印,"monitorWorker"只是告诉main函数它已经接收到了需要打印的所有数据。这样程序就可以优雅地结束,而不是死锁。

如果你坚持打印循环在另一个goroutine中,你可以这样做。但是这样就需要发送额外的通信给main函数,以便它返回。在下面的示例中,我使用一个通道来确保在打印所有数据后main函数结束。

package main

import (
	"fmt"
	"strconv"
	"sync"
)

func worker(wg *sync.WaitGroup, cs chan string, i int) {
	defer wg.Done()
	cs <- "worker" + strconv.Itoa(i)
}

func monitorWorker(wg *sync.WaitGroup, cs chan string) {
	wg.Wait()
	close(cs)
}

func printWorker(cs <-chan string, done chan<- bool) {
	for i := range cs {
		fmt.Println(i)
	}

	done <- true
}

func main() {
	wg := &sync.WaitGroup{}
	cs := make(chan string)

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

	go monitorWorker(wg, cs)

	done := make(chan bool, 1)
	go printWorker(cs, done)
	<-done
}
英文:

Your monitorWorker never dies. When all the workers finish, it continues to wait on cs. This deadlocks because nothing else will ever send on cs and therefore wg will never reach 0. A possible fix is to have the monitor close the channel when all workers finish. If the for loop is in main, it will end the loop, return from main, and end the program.

For example: http://play.golang.org/p/nai7XtTMfr

package main

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

func worker(wg *sync.WaitGroup, cs chan string, i int) {
	defer wg.Done()
	cs &lt;- &quot;worker&quot; + strconv.Itoa(i)
}

func monitorWorker(wg *sync.WaitGroup, cs chan string) {
	wg.Wait()
	close(cs)
}

func main() {
	wg := &amp;sync.WaitGroup{}
	cs := make(chan string)

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

	go monitorWorker(wg, cs)

	for i := range cs {
		fmt.Println(i)

	}
}

Edit: This is an answer to OP's first comment.

Your program has three parts that need to synchronize. First, all of your workers need to send the data. Then your print loop needs to print that data. Then your main function needs to return thereby ending the program. In your example, all the workers send the data, all the data gets printed, but the message is never sent to main that it should return gracefully.

In my example, main does the printing and "monitorWorker" just tells main when it has received every piece of data it needs to print. This way the program ends gracefully and not by deadlock.

If you insist on the print loop being in another goroutine, you can do that. But then an extra communication needs to be sent to main so it returns. In this next example, I use a channel to ensure main ends when all data is printed.

package main

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

func worker(wg *sync.WaitGroup, cs chan string, i int) {
	defer wg.Done()
	cs &lt;- &quot;worker&quot; + strconv.Itoa(i)
}

func monitorWorker(wg *sync.WaitGroup, cs chan string) {
	wg.Wait()
	close(cs)
}

func printWorker(cs &lt;-chan string, done chan&lt;- bool) {
	for i := range cs {
		fmt.Println(i)
	}

	done &lt;- true
}

func main() {
	wg := &amp;sync.WaitGroup{}
	cs := make(chan string)

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

	go monitorWorker(wg, cs)

	done := make(chan bool, 1)
	go printWorker(cs, done)
	&lt;-done
}

答案2

得分: 3

当我将通道定义从

strChan := make(chan string)

更改为

strChan := make(chan string, 1)

我成功修复了这个错误。

英文:

When I changed my channel definition from

strChan := make(chan string)

to

strChan := make(chan string, 1)

I was able to fix this error

答案3

得分: 0

如果你知道消息通道接收的数量,那么你可以简单地限制你的循环:

// c 是通道
for a := 1; a <= 3; a++ {
fmt.Println(<-c)
}

此外,你可以向工作程序传递另一个通道(工作程序的状态),然后有条件地停止导致死锁的循环。

附注:这只是一个额外的快速解决方案,并不特别针对你的解决方案。

英文:

If you know the count of the messages channel receives then simply you can limit your loop;

//c is channel
for a := 1; a &lt;= 3; a++{
		fmt.Println(&lt;-c)
}

Also you can pass another channel(status of the worker) to the worker then conditionally stop the loop that causes deadlock.

Ps: it's just an additional quick solution. Not specifically addresses your solution.

答案4

得分: 0

可以这样写,它会工作:

import "fmt"
import "sync"
import "strconv"

func worker(wg *sync.WaitGroup, cs chan string, i int) {
	defer wg.Done()
	cs <- "worker" + strconv.Itoa(i)
}

func monitorWorker(wg *sync.WaitGroup, cs chan string) {
	defer wg.Done()
	for i := range cs {
		fmt.Println(i)
	}
}

func main() {
	wg := &sync.WaitGroup{}
	cs := make(chan string, 10)

	go monitorWorker(wg, cs)

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

	wg.Wait()
	close(cs)
}

希望对你有帮助!

英文:

it can be like this also , it will work


import &quot;fmt&quot;
import &quot;sync&quot;
import &quot;strconv&quot;

func worker(wg *sync.WaitGroup, cs chan string, i int) {
	defer wg.Done()
	cs &lt;- &quot;worker&quot; + strconv.Itoa(i)

}

func monitorWorker(wg *sync.WaitGroup, cs chan string) {
	defer wg.Done()
	for i := range cs {
		fmt.Println(i)
	}
}
func main() {
	wg := &amp;sync.WaitGroup{}

	cs := make(chan string, 10)

  go monitorWorker(wg, cs)
  
	for i := 0; i &lt; 10; i++ {
		wg.Add(1)
		go worker(wg, cs, i)
	}
	wg.Wait()
	close(cs)
}

huangapple
  • 本文由 发表于 2013年11月11日 01:29:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/19892732.html
匿名

发表评论

匿名网友

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

确定