Golang 线程池
线程其实就是协程,但是还是说线程吧,习惯了
线程池最经典的demo感觉莫过于Java 的ThreadPool了,写得真的非常棒,过段时间要回顾下了,借鉴下
核心就是以下几点(暂时想到的):
- 有一个统一的分发类,也可以认为是池主体
- 池具体的对象
用Go来演示就是这样:
var JobQueue chan interface{}
type Worker interface {
io.Closer
Consume()
}
type BaseDispatcher struct {
workPool chan chan interface{}
workers []Worker // 保存了哪些worker
}
type PacketWorker struct {
workPool chan chan gopacket.Packet
jobQueue chan gopacket.Packet
}
核心方法:
func (d *Dispatcher) dispatch() {
for {
select {
case job := <-JobQueue:
go func(job Job) {
jobChannel := <-d.workPool
jobChannel <- job
}(job)
}
}
}
func (w *PacketWorker) Start() {
for {
w.workQueue <- w.jobQueue
select {
case job := <-w.jobQueue:
w.Consume(job)
}
}
}
可以认为有3个角色: job ,worker ,dispatcher
- job 毫无疑问:只是一个对象,内部则是处理的数据
- worker则是处理job的执行者
- dispatcher是分发任务,所有的生成的任务job,都会通过dispatcher入队到工作队列中
可能会有以下疑问:
- 为什么worker对象也要持有workPool的引用
- 为什么Dispatcher要持有所有worker的引用
- 为什么是chan chan类型
- 为什么会有一个JobQueue
- 为什么dispatch 方法是for 循环中创建线程,而不是有专门的几个线程分发任务
- dispatcher是如何做到分发的
答:
- 可以认为是永动机,而永动机形成就是通过这个持有引用形成的,当这个worker执行完毕他自己的jobQueue中的任务之后,就会跳出select,从而会重新入线程池,告诉dispatcher我的任务已经执行完毕了,并且此时这个worker会被自己的jobQueue阻塞,当重新入队之后,有任务来了,disaptcher就会出队一个jobQueue,某个worker持有这个引用,从而继续执行,所以这就是永动机,总结就是拿来收发任务的
- 线程通常都会携带某种资源,既占据内存,当线程结束总要释放资源,这个作用就是如此:对worker对象生命周期的管理
- 因为go中线程很廉价,但是廉价也不是随意乱用,这个的作用是起缓冲的作用
- 很简单,这个是作为一个中间者的,类似于交通工具上的卡车,用于传输数据,不然得到了数据怎么将其添加然后分发给worker工作呢
4.1 可能有新的疑问: 为什么只有一个JobQueue来接收数据,而不是多JobQueue来充当传输呢
答: 多个JobQueue无非就是当Job太多时,consumer速度跟不上producer速度,但是其实我们可以折中的,注意内部的element是interface{}不是具体的类型,也就意味着我们producer 可以做缓存,当缓存满的时候再入队,这是非常可取的,如果真的想创建多个JobQueue的话,不如将时间优化放在consumer上,当consumer时间开销小了,自然这个JobQueue1个也够了 - 这点其实就是Go有别于其他编程语言,如Java了,Java线程开销昂贵,Go却是很轻松,在Java中这样是不可取的
- dispatcher有一个自己的线程,基本上属于一个死循环,一旦JobQueue有任务来了,就会从线程池中出队一个worker(这点在Java中是通过数组实现的),这个worker不是真正的worker,而是被真正的worker所引用的一个存放job的通道,真正的worker也会是死循环,因为持有引用,所以当这个chan被添加任务之后,那worker也感知到了,就能消费了,当这个worker消费完毕之后,这个worker所持有的jobQueue又会重新入队,并且是非阻塞的,因为当我们初始化的时候是会指定workQueue的len的,并且当达到len的时候也没关系(这时候所有的worker都是阻塞在自身的jobQueue中的,因为都已经入workQueue了),当来新的job的时候(a这个jobQueue出队),对应的worker持有引用,消费完毕之后重新入队,就跟永动机一样
2019-01-05 22:30 有空再更,做项目
2019-01-07 23:23 更
- 今天突然想到一点,应该只有我自己能看懂了,就是当worker的jobQueue出队的时候代表着是有新的任务来了(来了新的任务就会出队),然后这个worker持有引用就是进行消费)这点其实与上面的一点是一致的
延伸
-
上面的模式又可以称为并行流水线模式这个模式其实是与Java中的CountDownLatch有类似的关系,都是分发任务
-
与之对应的则有:多个数据发给一个接收者,这种模式又与Java中的CyclicBarrier类似,都是用于数据采集的(注意:这里是数据结构采集,不是耗时的工作,都是总结的功能,这点与上面是不同的,上面的为耗时的工作分发给worker)