[英]Go Golang select statement cannot receive sended value
I'm new to Go and trying to implement a simple load balancer as seen in this slides: http://concur.rspace.googlecode.com/hg/talk/concur.html#slide-42 我刚接触Go,并尝试实现一个简单的负载均衡器,如本幻灯片所示: http : //concur.rspace.googlecode.com/hg/talk/concur.html#slide-42
The complete code: 完整的代码:
package main
import (
"fmt"
"time"
"container/heap"
)
type Request struct {
fn func(*Worker) int
c chan int
}
func requester(work chan <-Request) {
c := make(chan int)
work <- Request{workFn, c}
result := <-c
furtherProcess(result)
}
func workFn(w *Worker) int {
time.Sleep(1000 * time.Millisecond)
return w.index
}
func furtherProcess(result int) {
fmt.Println(result)
}
type Worker struct {
request chan Request
pending int
index int
}
func (w *Worker) work(done chan *Worker) {
for req := range w.request {
req.c <- req.fn(w)
fmt.Println("sending to done:", done)
done <- w
fmt.Println("sended to done")
}
}
type Pool []*Worker
type Balancer struct {
pool Pool
done chan *Worker
}
func (b *Balancer) balance(work chan Request) {
for {
fmt.Println("selecting, done:", b.done)
select {
case req := <-work:
b.dispatch(req)
case w := <-b.done:
fmt.Println("completed")
b.completed(w)
}
}
}
func (p Pool) Len() int {
return len(p)
}
func (p Pool) Less(i, j int) bool {
return p[i].pending < p[j].pending
}
func (p Pool) Swap(i, j int) {
p[i], p[j] = p[j], p[i]
}
func (p *Pool) Push(x interface{}) {
*p = append(*p, x.(*Worker))
}
func (p *Pool) Pop() interface{} {
old := *p
n := len(old)
x := old[n - 1]
*p = old[0 : n - 1]
return x
}
func (b *Balancer) dispatch(req Request) {
w := heap.Pop(&b.pool).(*Worker)
w.request <- req
w.pending++
heap.Push(&b.pool, w)
fmt.Println("dispatched to worker", w.index)
}
func (b *Balancer) completed(w *Worker) {
w.pending--
heap.Remove(&b.pool, w.index)
heap.Push(&b.pool, w)
}
func Run() {
NumWorkers := 4
req := make(chan Request)
done := make(chan *Worker)
b := Balancer{make([]*Worker, NumWorkers), done}
for i := 0; i < NumWorkers; i++ {
w := Worker{make(chan Request), 0, i}
b.pool[i] = &w
go w.work(done)
}
go b.balance(req)
for i := 0; i < NumWorkers * 4; i++ {
go requester(req)
}
time.Sleep(200000 * time.Millisecond)
}
func main() {
Run()
}
When I ran it, I got following outputs: 当我运行它时,我得到以下输出:
selecting, done: 0xc0820082a0
dispatched to worker 0
selecting, done: 0xc0820082a0
dispatched to worker 3
selecting, done: 0xc0820082a0
dispatched to worker 2
selecting, done: 0xc0820082a0
dispatched to worker 1
selecting, done: 0xc0820082a0
sending to done: 0xc0820082a0
sending to done: 0xc0820082a0
3
sending to done: 0xc0820082a0
2
1
0
sending to done: 0xc0820082a0
As you can see, it was selecting on and sending to the same pipe (done: 0xc0820082a0), but the select didn't receive the sended value and was blocking forever. 如您所见,它正在选择并发送到同一管道(已完成:0xc0820082a0),但是选择未接收到发送的值,并且一直处于阻塞状态。 How could this happen? 怎么会这样 What's the problem with the above code? 上面的代码有什么问题? Thanks! 谢谢!
Using kill -ABRT <PID>
you can see that all your Workers are blocked on done <- w
while your Balancer is blocked on w.request <- req
, creating a deadlock (workers can't go further until the balancer receives their "done" signals, and the balancer can't go further until the selected worker takes the request). 使用kill -ABRT <PID>
您可以看到您的所有Workers在done <- w
时都被阻止,而Balancer在w.request <- req
被阻止,从而产生了死锁(Worker在平衡器收到他们的“完成”信号,直到选定的工作人员接受请求后,平衡器才能继续前进。
If you replace done <- w
by go func() { done <- w }()
, you can see that your program will process the 16 requests without hanging. 如果用go func() { done <- w }()
替换done <- w
,您将看到您的程序将处理16个请求而不会挂起。
Side note: instead of time.Sleep(200000 * time.Millisecond)
, look into sync.WaitGroup
旁注:而不是time.Sleep(200000 * time.Millisecond)
,请查看sync.WaitGroup
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.