C++ 并发双阶段队列设计原理与实现

C++ 并发双阶段队列设计原理与实现

一、设计思想

该队列采用双阶段处理模型 ,通过锁机制实现线程安全,核心思想包含:

  1. 阶段隔离:入队(Enqueue)与处理(TryBegin/TryEnd)分离
  2. 原子操作:通过SpinLock保证关键段操作的原子性
  3. 状态跟踪:使用ID序列追踪任务处理状态
  4. 异常隔离:虚函数设计允许自定义处理逻辑

成功 成功 Enqueue 添加元素到list_ TryBegin 生成任务ID 保存到ends__ TryEnd 调用OnEnd处理


二、数据结构

1. 核心成员变量

成员变量 类型 作用
list_ chtrader::list<TElement> 存储待处理元素
ends__ unordered_map<int, ConcurrencyEndValue> 存储处理中的任务状态
skip_ unordered_set<int> 记录失败的任务ID
syncobj_ SpinLock 同步控制对象

2. 状态流转图

Enqueue元素 TryBegin获取任务 TryEnd成功 OnBegin失败 跳过任务 Idle Processing Completed Failed


三、流程分析

1. 入队流程 (Enqueue)

cpp 复制代码
virtual void Enqueue(TElement&& element) noexcept {
    SynchronizeObjectScope scope(syncobj_);
    list_.emplace_back(std::move(element));
}
  • 同步控制:通过SpinLock保护list_的push操作
  • 无拷贝设计 :使用std::move实现元素移动语义
  • 线程安全:保证多线程环境下的入队操作安全

2. 任务获取流程 (TryBegin)

cpp 复制代码
virtual bool TryBegin() noexcept {
    TElement element;
    int id;
    
    for (SynchronizeObjectScope scope(syncobj_);;) 
    {
        auto tail = list_.begin();
        auto endl = list_.end();
        if (tail == endl)
        {
            return false;
        }
        
        id = ++ends_id_inc_;
        element = std::move(*tail);
        
        list_.erase(tail);
        break;
    }
    
    ConcurrencyEndValue value;
    value.element = std::move(element);
    
    if (!OnBegin(value.element, value.result))
    {
        SynchronizeObjectScope scope(syncobj_);
        skip_.emplace(id);
        return false;
    }
    
    for (SynchronizeObjectScope scope(syncobj_);;)
    {
        auto r = ends__.emplace(std::make_pair(id, std::move(value)));
        return r.second;
    }
}
  • 双重检查:通过自增ID确保任务唯一性
  • 预处理机制:通过虚函数OnBegin实现自定义校验
  • 异常处理:失败任务ID存入skip_集合

3. 任务处理流程 (TryEnd)

cpp 复制代码
virtual bool TryEnd() noexcept {
    ConcurrencyEndValue value;
    for (SynchronizeObjectScope scope(syncobj_);;)
    {
        int next_id = ends_id_ + 1;
        auto tail = ends__.find(next_id);
        auto endl = ends__.end();
        if (tail == endl)
        {
            auto skip_tail = skip_.find(next_id);
            auto skil_endl = skip_.end();
            if (skip_tail != skil_endl)
            {
                ends_id_ = next_id;
                skip_.erase(skip_tail);
                return true;
            }

            return false;
        }

        value = std::move(tail->second);
        ends_id_ = next_id;
        ends__.erase(tail);
        break;
    }
    
    return OnEnd(value.element, value.result);
}
  • 顺序保证:通过ID自增保证处理顺序
  • 状态清理:处理完成后立即删除记录
  • 结果传递:通过引用参数返回处理结果

四、并发控制

1. 锁粒度

35% 45% 20% 锁粒度分布 list_操作 ends__操作 skip_操作

2. 优化点

  1. 批量处理:DoEvents()循环处理多个任务
  2. 内存预分配:list_使用连续内存存储
  3. 快速失败:skip_集合快速跳过无效任务

五、应用场景

Enqueue 生产者线程 ConcurrentTwoStepQueue 任务处理 IO密集型任务 计算密集型任务 异步存储 实时计算


六、源代码

cpp 复制代码
#pragma once 

// 包含预编译头文件(提高编译效率)
#include <chtrader/stdafx.h>

// 包含自旋锁实现(用于线程同步)
#include <chtrader/threading/SpinLock.h>

namespace chtrader 
{
   namespace collections 
   {
        // 并发双阶段队列模板类
        // TElement: 队列元素类型
        // TResult: 处理结果类型
        // SynchronizeObject: 同步机制类型(默认使用自旋锁)
        template <typename TElement, typename TResult, class SynchronizeObject = chtrader::threading::SpinLock>
        class ConcurrentTwoStepQueue
        {        
            // 用于存储待处理元素及其处理结果的结构体
            struct ConcurrencyEndValue
            {
                TElement                                             element;  // 原始元素
                TResult                                              result;    // 处理结果
            };

            // 锁作用域类型别名(简化锁管理)
            using SynchronizeObjectScope = std::lock_guard<SynchronizeObject>;

        public:
            // 默认构造函数
            ConcurrentTwoStepQueue() noexcept { }

            // 析构函数(确保所有事件被处理)
            virtual                                                     ~ConcurrentTwoStepQueue() noexcept
            {
                Clear();
            }

            // 入队操作(线程安全)
            void                                                        Enqueue(TElement&& element) noexcept
            {
                SynchronizeObjectScope scope(syncobj_);  // 加锁
                size_++;
                list_.emplace_back(std::move(element));  // 移动元素到队列尾部
            }

            // 开始处理元素(尝试获取下一个元素)   
            bool                                                        TryBegin() noexcept
            {
                TElement element;
                int sequence;

                // 循环尝试获取元素
                for (SynchronizeObjectScope scope(syncobj_);;)
                {
                    auto tail = list_.begin();  // 获取队列头部迭代器
                    auto endl = list_.end();    // 获取队列尾部迭代器

                    if (tail == endl)  // 队列为空时返回失败
                    {
                        return false;
                    }
                   
                    sequence = ++ends_inc_;  // 生成唯一ID
                    element = std::move(*tail);  // 移动元素值

                    list_.erase(tail);  // 从队列中移除元素
                    break;  // 退出循环
                }

                // 存储待处理元素及其结果
                ConcurrencyEndValue value;
                value.element = std::move(element);

                // 执行前置处理逻辑(需子类实现)
                bool initiate = OnBegin(value.element, value.result);
                if (unlikely(!initiate))
                {
                    SynchronizeObjectScope scope(syncobj_);
                    auto r = skip_.emplace(sequence);
                    assert(r.second);
                    return r.second;
                }

                // 存储到处理中的队列
                for (SynchronizeObjectScope scope(syncobj_);;)
                {
                    auto r = ends__.emplace(std::make_pair(sequence, std::move(value)));
                    assert(r.second);
                    return r.second;
                }
            }

            // 结束处理元素(尝试获取下一个待处理结果) 
            bool                                                        TryEnd() noexcept
            {
                ConcurrencyEndValue value;
                for (SynchronizeObjectScope scope(syncobj_);;)
                {
                    auto ack = ends_ack_.load(std::memory_order_acquire);
                    auto ends_tail = ends__.find(ack);  // 查找对应ID的元素
                    if (ends_tail == ends__.end())  // 未找到时检查跳过集合
                    {
                        auto skip_tail = skip_.find(ack);
                        if (skip_tail != skip_.end())  // 存在跳过记录则移除
                        {
                            skip_.erase(skip_tail);
                            
                            size_.fetch_sub(1, std::memory_order_release);
                            ends_ack_.fetch_add(1, std::memory_order_release);

                            return true;  // 表示跳过成功
                        }

                        return false;  // 队列空且无跳过项时返回失败
                    }

                    value = std::move(ends_tail->second);  // 获取元素值
                    ends__.erase(ends_tail);  // 从处理队列中移除
                    
                    size_.fetch_sub(1, std::memory_order_release);
                    ends_ack_.fetch_add(1, std::memory_order_release);
                    
                    // 执行后置处理逻辑(需子类实现)
                    OnEnd(value.element, value.result);
                    return true;
                }
            }

            // 处理所有可用事件(批量处理)
            int                                                         DoEvents() noexcept 
            {
                for (int events = 0;;) 
                {
                    TryBegin();
                    if (TryEnd())
                    {
                        events++;
                    }
                    else 
                    {
                        return events;  // 返回处理的事件总数
                    }
                }
            }

            // 清空队列
            void                                                        Clear() noexcept 
            {
                chtrader::unordered_map<int, ConcurrencyEndValue> ends;
                chtrader::unordered_set<int> skip;
                chtrader::list<TElement> list;

                for (SynchronizeObjectScope scope(syncobj_);;)
                {
                    list = std::move(list_);
                    skip = std::move(skip_);
                    ends = std::move(ends__);

                    list_.clear();
                    skip_.clear();
                    ends__.clear();
                    break;
                }
            }

            int                                                         Count() noexcept { return size_.load(std::memory_order_acquire); }

        protected:   
            // 前置处理回调(需子类实现具体逻辑)    
            virtual bool                                                OnBegin(const TElement& element, TResult& output) noexcept = 0;

            // 后置处理回调(需子类实现具体逻辑)                    
            virtual void                                                OnEnd(const TElement& element, const TResult& result) noexcept = 0;
           
        private: 
            SynchronizeObject                                           syncobj_;               // 同步锁对象
            chtrader::list<TElement>                                    list_;                  // 底层元素队列
            chtrader::unordered_set<int>                                skip_;                  // 跳过处理的ID集合

            std::atomic<int>                                            size_        = 0;       // 数量
            std::atomic<int>                                            ends_ack_    = 1;       // 当前处理的ID
            std::atomic<int>                                            ends_inc_    = 0;       // ID递增计数器
            chtrader::unordered_map<int, ConcurrencyEndValue>           ends__;                 // 处理中的元素映射
       };
   }
}
相关推荐
YFLICKERH1 小时前
【Linux系统】磁盘文件系统
linux
森G1 小时前
五、Linux字符设备驱动
linux·arm开发·c++·ubuntu
生信大表哥1 小时前
Claude Code / Gemini CLI / Codex CLI 安装大全(Linux 服务器版)
linux·python·ai·r语言·数信院生信服务器
lly2024061 小时前
并查集快速查找
开发语言
繁星蓝雨1 小时前
我与C++的故事(杂谈)
开发语言·c++
知识分享小能手1 小时前
CentOS Stream 9入门学习教程,从入门到精通, Linux文本编辑器 —— 语法详解与实战案例(5)
linux·学习·centos
白狐_7981 小时前
【项目实战】我用一个 HTML 文件写了一个“CET-6 单词斩”
前端·算法·html
Jasmine_llq1 小时前
《P3811 【模板】模意义下的乘法逆元》
数据结构·算法·线性求逆元算法·递推求模逆元
除了代码啥也不会1 小时前
Java基于SSE流式输出实战
java·开发语言·交互