入门级的并行加速一般会以一个大数组的计算来举例。这种并行加速的方式适用于大量数据的复杂计算,而且这些计算必须是重复的。然而大多数的程序并不会遇到大量数据的相同计算,这种入门级的并行加速就显得无用武之地。
其实并行加速的模型是对一个任务矩阵进行分块。这个矩阵有2个维度:时间维度和空间维度。
入门级的并行加速是对空间维度进行分片。而大多数的程序并不会同时触发一大批的任务。这些任务是随机启动的。入门级的并行加速要求任务同时启动,那么就必须对随机出现的任务进行缓存。而任务触发的周期有长有短,为了防止缓存长时间不能充满,还需要一个定时器进行强制触发。虽然看起来很好地解决了问题,但是使用这种并发结构必然导致响应延迟,CPU的负载不均匀,降低服务器的处理能力。
相对高级的方法是使用线程池。线程池很好地消除了对同时启动的限制。但是无限线程池容易会造成内存大量消耗,不知道什么时候内存就可能耗尽,实际运行的线程也就是进程绑定的CPU逻辑核心数,并不能无限加速。固定线程池能很好地控制运行线程数量,防止大量线程交换造成过多的计算,但是固定线程池在IO处理的时候力不从心。使用线程池虽然高级一些,却不是理想的解决办法。
具有工程意义的并行加速主要是对时间维度进行分片。将一个长任务分解成一系列的步骤,每一步骤根据计算量和IO调用的情况分别设置固定数量的线程。线程之间的数据采用队列或映射进行传送。收到请求之时,可以立即开始处理。
特殊的情况下,线程之间的数据可以采用其它容器实现。比较特别的是映射。映射会自动清除同一个键下,未被处理的旧数据。这一特性在互联网服务中是匪夷所思的,因为互联网服务不会在未触发异常的情况下主动清除未完成的用户请求。映射主要适用于窗口任务的传递。操作系统只有一个显示线程。这个线程的优先级最低,而且睡眠时也会暂停。如果对窗口任务使用队列传递,就很容易在难以预料到的情况下触发内存溢出导致显示异常,甚至把操作系统都卡死。这时,使用映射作为数据传输的载体就能非常简单地避免内存溢出。
对时间维度进行任务分割的方式,非常适合处理随机产生的高并发请求。这种架构的名称即是流水线。
一条流水线可以包含2种抽象的成员类型:工作组和传送器。
工作组即是业务代码中对特定任务片段的实现,并以指定数量的线程承载运行。
传送器是将上游工作员的计算结果(半成品数据)传递到下游工作员的子程序,一般是具有阻塞功能的容器。
工作组中的每一个线程称为工作员。
由于每个工作组都只执行完整业务的某个片段,每个工作组的业务代码都相对简单,这就提高了业务代码片段的维护性。
工作组中的工作员数量经计算或测试来决定,对业务中执行时间较短的部分设置少量工作员,对业务中执行时间较长的部分设置大量工作员,合理安排线程数量,可充分发挥CPU的算力。
由于传送器的存在,监视线程能够检测到任务积压的情况,这就可以提前预判内存溢出和超时异常,及时止损,清理掉那些没有希望完成的请求或暂停受理请求,保障系统稳定性。