2016-11-04 35 views
1

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

在我的主要功能中,我启动处理程序并将9个元素写入queue,并等待第一个元素写入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

+1

乍一看你写'完成“至少5次,但只读一次。 – myaut

+0

@myaut,这是意图。我只对第一个正在写入数据的实例感兴趣。 – Pratyush

回答

1

您阻止迭代过与在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) { 
    // Start handlers 
    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() 
} 

https://play.golang.org/p/oUFjZONjhk(注意,在操场上,这个例子似乎目前倾向于单一的goroutine是接收器。通常情况下,阻止够程将获得随机的,你可以看到的行为,如果你编译并正常运行)

+0

感谢WaitGroup的回答。我认为这是一个更好的解决方案来同步多个goroutines。 – Pratyush

+0

“_因为另一端没有任何东西正在接收_” 根据代码,有一行实际上在另一端接收数据'fmt.Println(“...........>”, < - 完成)'。我在执行顺序中移动了排队,并且工作正常。我想一个渠道需要消费者在写数据的时候,而不是在将来的某个时候。 – Pratyush

+0

@Pratyush:是的,有1个接收,但你需要不断收到,以保持循环进行。 – JimB

1

在for循环中,您只处理第5个元素的通道操作,但是在主函数中,您尝试将值发送到已关闭的通道。

为了克服这种情况下,你可以发送请求值内的循环:

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

+1

如果我将main函数中的for循环更改为'for i:= 0;我<100; i ++'我得到了死锁错误 - https://play.golang.org/p/rT2aa-sllX – Pratyush

+1

我将代码移出执行顺序中的“完成”队列,似乎工作正常。 https://play.golang.org/p/UxCu1220-- – Pratyush

+1

@Pratyush:最后并没有真正与最终的<<-done>一起工作,因为你正在争夺另一个死锁:https://play.golang .ORG/p/_hzsC3LKIs – JimB