一、场景说明
我们总是会有执行批量任务的场景,这种场景其实可以分为若干场景,我们仔细分析一下:
1. 实时任务:任务不知道什么时候会到,有可能一分钟来一条,也有可能一秒钟来200条,无法预知任务什么时候会到,只能来到任务之后丢到线程池计算
2. 可预知任务:能够准确知道任务到达,并能够分解出确切的子任务,这种任务同时只会做一次,本文主要是针对这种情况并发执行给出一种更为优雅的解决方式
如上图:
第一种方式是任务来到之后直接提交到线程池,线程池会有一个队列存储任务,每一个线程都在从队列中抓取任务并且消费掉,直到任务队列为空就歇着。
第二种方式更像是流水线作业,每一个任务都会有一个序列号,如果我们希望三个并发,那么我们一共有三个线程,分别编号0、1、2,任务总数一共是6个,编号从1开始,所以我们非常容易就可以得到:
0号线程处理:3、6
1号线程处理:1、4
2号线程处理:2、5
这种方式处理并发其实更加容易,更像是spark中RDD的抽象模型,数据虽然会读多遍,但是整体性能上由于是分布式并行处理,反而速度回很快
二、常用方式——流水线处理法
concurrentLevel := N
waitGroup.Add(concurrentLevel)
for i := 0; i < concurrentLevel; i++ {
index := i
go func() {
defer func() {
waitGroup.Done()
}()
for i := 0; i < len(tasks); i++ {
if tasks[i].ID % concurrentLevel != index{
continue
}
// 合适的线程找到合适的任务
xxxxxxx
}
}()
}
waitGroup.Wait()
总的来说,流水线并发处理方式我认为在处理单次固定任务列表比线程池更加优雅