goroutine asleep – deadlock(协程休眠 – 死锁)

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

goroutine asleep - deadlock

问题

在下面的代码中,我试图生成MaxOutstanding数量的处理程序。每个处理程序循环遍历队列queue中的项目并将其打印出来,我还向done通道写入true

在我的主函数中,我启动处理程序并向queue写入9个元素,并等待第一个元素被写入done队列。

package main


import "fmt"

type Request struct {
        int32
}
var MaxOutstanding = 5
func handle(queue chan *Request, i int, done chan bool) {
    for r := range queue {
        fmt.Println(i, "---", r)
        done <- true
    }
}

func Serve(clientRequests chan *Request, quit, done chan bool) {
    // Start handlers
    for i := 0; i < MaxOutstanding; i++ {
        go handle(clientRequests, i, done)
    }
    <-quit  // Wait to be told to exit.
}


func main() {
    clientRequests := make(chan *Request)
    quit := make(chan bool)
    done := make(chan bool)
    
    go Serve(clientRequests, quit, done)
    
    clientRequests <- &Request{4}
    clientRequests <- &Request{1}
    clientRequests <- &Request{2}
    clientRequests <- &Request{3}
    
    clientRequests <- &Request{5}
    clientRequests <- &Request{6}
    clientRequests <- &Request{7}
    clientRequests <- &Request{8}
    clientRequests <- &Request{9}
    fmt.Println("...........>", <-done)
    close(clientRequests)
    close(done)
}

执行时,我得到以下错误。我不明白实现有什么问题,我甚至关闭了通道。

4 --- &{4}
0 --- &{1}
1 --- &{2}
2 --- &{3}
3 --- &{5}
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main()
        /home/ubuntu/digs-svc/src/digs/go1.go:45 +0x251

goroutine 5 [chan receive]:
main.Serve(0xc82004c060, 0xc82004c0c0, 0xc82004c120)
        /home/ubuntu/digs-svc/src/digs/go1.go:28 +0x92
created by main.main
        /home/ubuntu/digs-svc/src/digs/go1.go:37 +0xb9

goroutine 6 [chan send]:
main.handle(0xc82004c060, 0x0, 0xc82004c120)
        /home/ubuntu/digs-svc/src/digs/go1.go:16 +0x23b
created by main.Serve
        /home/ubuntu/digs-svc/src/digs/go1.go:25 +0x5b

编辑:

显然,fmt.Println("....", <-done)不足以表示done通道有一个消费者。我将代码移到了执行顺序的前面。当数据被写入通道时,消费者需要“监听”该通道。在我之前的代码中,当第一个数据被写入时,没有消费者。

工作代码。

https://play.golang.org/p/98l2M4XO9t

英文:

In the following code I am trying to spawn MaxOutstanding number of handlers. Each handler loops over items in the queue queue and prints them out, I also write true to the done channel.

In my main function, I start the handlers and write 9 elements to the queue and wait for the 1st element to be written to the done queue.

package main


import &quot;fmt&quot;

type Request struct {
        int32
}
var MaxOutstanding = 5
func handle(queue chan *Request, i int, done chan bool) {
    for r := range queue {
        fmt.Println(i, &quot;---&quot;, r)
        done &lt;- true
    }
}

func Serve(clientRequests chan *Request, quit, done chan bool) {
    // Start handlers
    for i := 0; i &lt; MaxOutstanding; i++ {
        go handle(clientRequests, i, done)
    }
    &lt;-quit  // Wait to be told to exit.
}


func main() {
    clientRequests := make(chan *Request)
    quit := make(chan bool)
    done := make(chan bool)
    
    go Serve(clientRequests, quit, done)
    
    clientRequests &lt;- &amp;Request{4}
    clientRequests &lt;- &amp;Request{1}
    clientRequests &lt;- &amp;Request{2}
    clientRequests &lt;- &amp;Request{3}
    
    clientRequests &lt;- &amp;Request{5}
    clientRequests &lt;- &amp;Request{6}
    clientRequests &lt;- &amp;Request{7}
    clientRequests &lt;- &amp;Request{8}
    clientRequests &lt;- &amp;Request{9}
    fmt.Println( &quot;...........&gt;&quot;, &lt;- done )
    close(clientRequests)
    close(done)
}

On execution I get the following error. I don't see whats wrong with the implementation, I am even closing the channel.

4 --- &amp;{4}
0 --- &amp;{1}
1 --- &amp;{2}
2 --- &amp;{3}
3 --- &amp;{5}
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main()
        /home/ubuntu/digs-svc/src/digs/go1.go:45 +0x251

goroutine 5 [chan receive]:
main.Serve(0xc82004c060, 0xc82004c0c0, 0xc82004c120)
        /home/ubuntu/digs-svc/src/digs/go1.go:28 +0x92
created by main.main
        /home/ubuntu/digs-svc/src/digs/go1.go:37 +0xb9

goroutine 6 [chan send]:
main.handle(0xc82004c060, 0x0, 0xc82004c120)
        /home/ubuntu/digs-svc/src/digs/go1.go:16 +0x23b
created by main.Serve
        /home/ubuntu/digs-svc/src/digs/go1.go:25 +0x5b

EDIT:

Apparently, the fmt.Println(&quot;....&quot;, &lt;- done) wasn't enough to signify that there is a consumer to the done channel. I moved the code up in the execution order. A consumer needs to be "listening" on the channel when data is written to it. In my earlier code, the when the first data was written there were no consumer.

Working code.

https://play.golang.org/p/98l2M4XO9t

答案1

得分: 1

你的 handle 函数中的 done 通道的发送操作阻塞了对通道的迭代,因为没有任何地方接收该通道的数据。

那些额外的通道实际上没有起到任何作用,你可以添加一个 WaitGroup 来同步处理程序的退出,然后可以移除 done 通道,这样处理程序就可以继续执行。

func handle(queue chan *Request, i int, wg *sync.WaitGroup) {
    defer wg.Done()

    for r := range queue {
        fmt.Println(i, "---", r)
    }
}

func Serve(clientRequests chan *Request, wg *sync.WaitGroup) {
    // 启动处理程序
    for i := 0; i < MaxOutstanding; i++ {
        wg.Add(1)
        go handle(clientRequests, i, wg)
    }
}

func main() {
    clientRequests := make(chan *Request)
    var wg sync.WaitGroup

    go Serve(clientRequests, &wg)

    for i := int32(0); i < 50; i++ {
        clientRequests <- &Request{i}
    }

    close(clientRequests)
    wg.Wait()
}

请注意,在 playground 中,该示例似乎偏向于单个 goroutine 成为接收者。通常情况下,被阻塞的 goroutine 会随机接收数据,如果你编译并正常运行,就可以看到这种行为。

英文:

You're blocking the iteration over the channel in your handle function with the send on the done channel, because nothing is receiving on the other side.

Those extra channels aren't really doing anything, and you could just add a WaitGroup to synchronize the handler's exit, then you can remove the done channel which will allow the handler to continue.

func handle(queue chan *Request, i int, wg *sync.WaitGroup) {
	defer wg.Done()

	for r := range queue {
		fmt.Println(i, &quot;---&quot;, r)
	}
}

func Serve(clientRequests chan *Request, wg *sync.WaitGroup) {
	// Start handlers
	for i := 0; i &lt; MaxOutstanding; i++ {
		wg.Add(1)
		go handle(clientRequests, i, wg)

	}
}

func main() {
	clientRequests := make(chan *Request)
	var wg sync.WaitGroup

	go Serve(clientRequests, &amp;wg)

	for i := int32(0); i &lt; 50; i++ {
		clientRequests &lt;- &amp;Request{i}
	}

	close(clientRequests)
	wg.Wait()
}

https://play.golang.org/p/oUFjZONjhk (note that in the playground, this example seems to currently favor a single goroutine being the receiver. Normally the blocked goroutines will receive randomly, and you can see that behavior if you compile and run normally)

答案2

得分: 1

在for循环中,你只处理了第5个元素的通道操作,然而在主函数中,你试图向通道发送值,但该通道已关闭。

为了解决这个问题,你可以在for循环中发送请求值:

for i := 0; i < MaxOutstanding; i++ {
    clientRequests <- &Request{int32(i)}
}

以下是工作的代码:

package main

import (
    "fmt"
)

type Request struct {
    int32
}

var MaxOutstanding = 10

func handle(queue chan *Request, i int, done chan bool) {
    for r := range queue {
        fmt.Println(i, "---", r)
        done <- true
    }
}

func Serve(clientRequests chan *Request, quit, done chan bool) {
    // Start handlers
    for i := 0; i < MaxOutstanding; i++ {
        go handle(clientRequests, i, done)
    }
    <-quit // Wait to be told to exit.
}

func main() {
    clientRequests := make(chan *Request)
    quit := make(chan bool)
    done := make(chan bool)

    go Serve(clientRequests, quit, done)
    for i := 0; i < MaxOutstanding; i++ {
        clientRequests <- &Request{int32(i)}
    }

    fmt.Println("...........>", <-done)
    close(clientRequests)
    close(done)
}

你可以在这里查看代码:https://play.golang.org/p/L5Y2YoFNvz

英文:

Inside the for loop you are handling the channel operation only to the 5th element, however in the main function you are trying to send over the value to the channel, which is closed.

To overcome this situation you can send the request value inside a for loop:

for i := 0; i &lt; MaxOutstanding; i++ {
		clientRequests &lt;- &amp;Request{int32(i)}
}

Here is the working code:

package main

import (
	&quot;fmt&quot;
)

type Request struct {
	int32
}

var MaxOutstanding = 10

func handle(queue chan *Request, i int, done chan bool) {
	for r := range queue {
		fmt.Println(i, &quot;---&quot;, r)
		done &lt;- true
	}
}

func Serve(clientRequests chan *Request, quit, done chan bool) {
	// Start handlers
	for i := 0; i &lt; MaxOutstanding; i++ {
		go handle(clientRequests, i, done)
	}
	&lt;-quit // Wait to be told to exit.
}

func main() {
	clientRequests := make(chan *Request)
	quit := make(chan bool)
	done := make(chan bool)

	go Serve(clientRequests, quit, done)
	for i := 0; i &lt; MaxOutstanding; i++ {
		clientRequests &lt;- &amp;Request{int32(i)}
	}

	fmt.Println(&quot;...........&gt;&quot;, &lt;-done)
	close(clientRequests)
	close(done)
}

https://play.golang.org/p/L5Y2YoFNvz

huangapple
  • 本文由 发表于 2016年11月4日 21:06:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/40423461.html
匿名

发表评论

匿名网友

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

确定