我从google io 2010获取了loadbalancer代码,并添加了针对Balancer的优先级队列和同步锁定的实现。我故意将workFn
函数延迟设置为大于requester
,因此我可以看到如何增加挂起值。我在cli中运行它并注意到在所有工作人员启动后,程序因所有工人的待定值 1 而停止,并且没有显示任何内容。错误在哪里我无法弄清楚,有时completed
只召唤一次或两次。看起来<-b.done
在特定情况下未得到妥善处理。
package main
import (
"container/heap"
"fmt"
"math/rand"
"os"
"sync"
"time"
)
var nWorker int32 = 6
func main() {
rchanel := make(chan Request)
workers := Pool{
{make(chan Request), 0, 0},
{make(chan Request), 0, 1},
{make(chan Request), 0, 2},
{make(chan Request), 0, 3},
{make(chan Request), 0, 4},
{make(chan Request), 0, 5},
}
doneChan := make(chan *Worker)
balancer := Balancer{workers, sync.Mutex{}, doneChan}
for _, elem := range workers {
go elem.work(doneChan)
}
go balancer.balance(rchanel)
go requester(rchanel)
var input string
fmt.Scanln(&input)
}
type Request struct {
fn func() int
c chan int
}
func requester(work chan Request) {
c := make(chan int)
for {
time.Sleep(time.Duration(rand.Int31n(nWorker)) * 2e4)
work <- Request{workFn, c}
go func() {
result := <-c
fmt.Fprintf(os.Stderr, "Done: %v \n", result)
}()
}
}
func workFn() int {
val := rand.Int31n(nWorker)
time.Sleep(time.Duration(val) * 2e8)
return int(val)
}
type Worker struct {
requests chan Request
pending int
index int
}
func (w *Worker) work(done chan *Worker) {
for {
req := <-w.requests
req.c <- req.fn()
done <- w
}
}
type Pool []*Worker
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]
p[i].index = i
p[j].index = j
}
func (p Pool) Len() int { return len(p) }
func (p *Pool) Push(x interface{}) {
n := len(*p)
worker := x.(*Worker)
worker.index = n
*p = append(*p, worker)
}
func (p *Pool) Pop() interface{} {
old := *p
n := len(old)
item := old[n-1]
item.index = -1
*p = old[0 : n-1]
return item
}
type Balancer struct {
pool Pool
mu sync.Mutex
done chan *Worker
}
func (b *Balancer) dispatch(req Request) {
b.mu.Lock()
w := heap.Pop(&b.pool).(*Worker)
w.requests <- req
w.pending++
heap.Push(&b.pool, w)
b.mu.Unlock()
}
func (b *Balancer) completed(w *Worker) {
b.mu.Lock()
w.pending--
heap.Remove(&b.pool, w.index)
heap.Push(&b.pool, w)
b.mu.Unlock()
}
func (b *Balancer) balance(work chan Request) {
for {
select {
case req := <-work:
b.dispatch(req)
b.printStatus()
case w := <-b.done:
b.completed(w)
b.printStatus()
}
}
}
func (b *Balancer) printStatus() {
fmt.Fprintf(os.Stderr, "Status: %v %v %v %v %v %v\n", b.pool[0].pending, b.pool[1].pending, b.pool[2].pending, b.pool[3].pending, b.pool[4].pending, b.pool[5].pending)
}
答案 0 :(得分:1)
问题是balance()
goroutine最终在dispatch()
上的w.requests <- req
被阻止,同时Worker
在work()
上阻止了done <- w
{1}},为运行balance()
的goroutine产生死锁。
这是您需要的修复程序。 balance()
需要在内部使用goroutine。这将解决问题,因为现在dispatch()
或completed()
中的例程阻塞无关紧要,balance()
的主例程将继续select
来自channel
1}} S上。
注意:这在游乐场不起作用,因为它会永远持续下去。
func (b *Balancer) balance(work chan Request) {
for {
select {
case req := <-work:
go func() {
b.dispatch(req)
b.printStatus()
}()
case w := <-b.done:
go func() {
b.completed(w)
b.printStatus()
}()
}
}
}
现在可以同时进行printStatus
次调用,它也需要使用mutex
,否则您将获得随机panic
。
func (b *Balancer) printStatus() {
b.mu.Lock()
fmt.Fprintf(os.Stderr, "Status: %v %v %v %v %v %v\n", b.pool[0].pending, b.pool[1].pending, b.pool[2].pending, b.pool[3].pending, b.pool[4].pending, b.pool[5].pending)
b.mu.Unlock()
}
现在,如果我能弄清楚为什么pending
值会不断增加......据我所知,Worker.work()
应该只允许pending
为{{1}或0
因为1
必须等待Worker
才能从done <- w
获得另一个Request
。我相信这是理想的结果,不是吗?