← Back to Blog
EN中文

串行后处理队列的并行-串行混合架构

在并发编程中,有些任务需要先并行处理再串行汇总。今天我们深入分析一个工业级 串行后处理队列的实现,探索其 并行-串行混合架构 的设计智慧。

场景与需求

这种两阶段处理模式的应用场景:

  • 批量数据处理:先并行计算,再串行写入
  • 日志收集:先并行聚合,再串行落盘
  • 报表生成:先并行统计,再串行生成报告

核心需求:

  • 高性能:第一阶段需要并行处理提升吞吐
  • 顺序保证:第二阶段需要串行确保顺序
  • 资源控制:需要队列大小限制防止内存溢出

解决方案:两阶段队列设计

工业级实现采用了独特的两阶段架构:

// 核心设计:两个队列
IThreadPool* ParallelQueue;  // 并行处理队列
THolder<IThreadPool> SerialQueue;  // 串行处理队列

// 任务包装
class TAsync {
    void Process(void* threadSpecificResource) override {
        WorkItem->ParallelProcess();
        Done();  // 标记完成
    }
};

class TSync {
    void Process(void*) override {
        Async.Wait();  // 等待并行完成
        WorkItem->SerialProcess();  // 串行后处理
    }
};

核心设计

  1. TAsync:并行处理

    • 在线程池中并行执行
    • 完成后通过条件变量通知
  2. TSync:串行包装

    • 先加入串行队列
    • 等待 TAsync 完成
    • 执行 SerialProcess()
  3. Add 流程

    bool Add(TAutoPtr<IProcessObject> obj) {
        TSync* sync = new TSync(obj);
        // 先加入串行队列
        SerialQueue->Add(sync);
        // 再加入并行队列
        IObjectInQueue* async = sync->GetAsync();
        ParallelQueue->Add(async);
    }

权衡分析

优点

  1. 高性能:并行阶段利用多核
  2. 顺序保证:串行阶段确保顺序
  3. 资源控制:可配置队列大小

代价

  1. 复杂度:两阶段协调增加复杂度
  2. 延迟:串行阶段可能成为瓶颈
  3. 内存:任务需要保持直到串行完成

适用场景

  • 需要先并行后串行的任务
  • 顺序敏感的后处理
  • 大批量数据处理

净室重构:Go 实现

下面用 Go 展示同样的设计思想:

type SerialPostProcessQueue struct {
    parallelPool *WorkerPool
    serialQueue chan *SerialTask
}

// 两阶段处理
func (q *SerialPostProcessQueue) Add(obj ProcessObject) {
    task := &SerialTask{
        object:    obj,
        completed: make(chan bool, 1),
    }
    
    // 阶段1: 并行处理
    q.parallelPool.Submit(func() {
        obj.ParallelProcess()
        task.completed <- true
    })
    
    // 阶段2: 串行处理
    q.serialQueue <- task
}

// 串行 worker
go func() {
    for task := range q.serialQueue {
        <-task.completed  // 等待并行完成
        task.object.SerialProcess()  // 串行后处理
    }
}()

运行结果验证了设计:

=== Serial-PostProcess Queue Demo (Go) ===

--- Adding Tasks ---
Added task 1
Added task 2
Added task 3
Added task 4
Added task 5

--- Processing (parallel first, then serial) ---
[Parallel] Task 1 processing...
[Parallel] Task 4 processing...
[Parallel] Task 2 processing...
[Parallel] Task 3 processing...
[Parallel] Task 3 parallel done
[Parallel] Task 5 processing...
...
[Serial] Task 1 post-processing...
[Serial] Task 2 post-processing...
[Serial] Task 3 post-processing...
...

总结

串行后处理队列的并行-串行混合架构体现了 吞吐与顺序的平衡 的工程智慧:

  1. 两阶段设计:并行提升吞吐,串行保证顺序
  2. 任务包装:TSync 等待 TAsync 完成
  3. 资源控制:队列大小限制
  4. 优雅停机:支持 Stop 等待所有任务完成

这种设计不是"银弹",但对于需要先并行后串行的场景,是一个非常优雅的权衡选择。