bilibili视频上看的一个面试题, 看了很久, 也想了很久, 自己平时都是做web后端开发,很少接触到并发编程, 最后困难的实现了一下:
描述: 实现一个负载器, 可以注册多个worker工作, 每个worker的工作能力不同, lb也不能阻塞. 哔哩哔哩网址
package test_dir
import (
"fmt"
"testing"
"time"
)
var workerQueue = make(chan *worker)
type loadBalance struct {
tasks chan func(int)
}
type worker struct {
id int
queue chan func(int)
}
func (w *worker) work() {
for t := range w.queue {
t(w.id)
}
}
func (w *worker) submit(task func(int)) {
w.queue <- task
}
func (l *loadBalance) submit(task func(int)) {
go func() { l.tasks <- task }() //不可阻塞LB
}
func (l *loadBalance) wait() {
go func() {
for {
w := <-workerQueue // 获取一个worker, 没有可用的会阻塞在这
f := <-l.tasks // 获取一个task任务, 没有也会阻塞
go func() {
w.submit(f) // 提交任务给worker, 如果塞不进去会阻塞当前worker重回workerQueue
workerQueue <- w
}()
}
}()
select {}
}
// 注册负载
func LB(workerPowerNum ...int) *loadBalance {
for k, v := range workerPowerNum {
worker := &worker{queue: make(chan func(int), v), id: k + 1}
// 添加到ch
go func() { workerQueue <- worker }()
go worker.work()
}
return &loadBalance{tasks: make(chan func(int))}
}
func Test_LB(t *testing.T) {
lb := LB(4, 1, 2, 5, 6)
for i := 0; i < 20; i++ {
lb.submit(func(v int) func(int) {
return func(i int) {
time.Sleep(time.Second)
fmt.Println("任务执行完成: ", v, "机器编号", i)
}
}(i))
}
lb.wait()
}
输出:
=== RUN Test_LB
任务执行完成: 0 机器编号 2
任务执行完成: 1 机器编号 3
任务执行完成: 4 机器编号 1
任务执行完成: 2 机器编号 4
任务执行完成: 3 机器编号 5
任务执行完成: 19 机器编号 3
任务执行完成: 12 机器编号 5
任务执行完成: 18 机器编号 2
任务执行完成: 11 机器编号 1
任务执行完成: 16 机器编号 1
任务执行完成: 17 机器编号 2
任务执行完成: 5 机器编号 5
任务执行完成: 13 机器编号 1
任务执行完成: 6 机器编号 5
任务执行完成: 14 机器编号 1
任务执行完成: 7 机器编号 5
任务执行完成: 15 机器编号 1
任务执行完成: 8 机器编号 5
任务执行完成: 9 机器编号 5
任务执行完成: 10 机器编号 5
自己跑测试结果能基本符合,也不知道是不是符合题意, 但是视频最后博主说要实现LB和worker的状态分离: 我自己用了第三个变量workerQueue ::>_<::,技穷了
, 不要暴力检测的: 各种锁什么的临界
, 大家可以各自实现一下完美需求.
最后发现用select{}阻塞进程会报死锁异常, 不知到大家为啥不会报死锁~~