如何为C++限价订单簿匹配函数实现参数化线程锁?
C++限价订单簿_match_orders多线程访问优化方案
核心需求
仅对同一个LimitLevel对象的_match_orders调用进行串行阻塞,不同LimitLevel对象的调用允许完全并发执行,既保证线程安全,又最大化多线程利用效率。
现有方案缺陷
- 全局
std::mutex导致所有调用串行,完全浪费多线程优势; - 用
unordered_set记录占用价格+condition_variable的方案,频繁的集合读写会引入额外性能开销; - 为每个可能价格预创建mutex的方案,因价格范围达
INT_MAX,资源开销完全不可接受。
最优解决方案:每个LimitLevel内置专属mutex
思路
由于LimitLevel仅在对应价格存在订单时才会创建(而非覆盖所有可能价格),给每个LimitLevel实例绑定一个专属std::mutex,既能实现精准的锁粒度(只保护需要同步的对象),又不会造成资源浪费。同时,用一个全局mutex专门保护共享的全局结构(如orders哈希表、bids/asks价格树),将锁的范围最小化。
代码修改步骤
修改LimitLevel类,添加专属mutex
给LimitLevel增加私有mutex成员,并提供访问接口:class LimitLevel final { public: const int price; std::queue<int> *orders; int quantity; LimitLevel(Order *order) : price(order->price), quantity(order->quantity) { orders = new std::queue<int>(); orders->push(order->id); } ~LimitLevel() { delete orders; } // 获取专属mutex的接口 std::mutex& get_mutex() { return mtx; } // 原有方法保持不变 inline void append(Order *order) { orders->push(order->id); } inline void append(int _id) { orders->push(_id); } inline int pop_left() { int val = orders->front(); orders->pop(); return val; } inline int get_head() { if (get_length()) { return orders->front(); } else { throw std::length_error("Head attempted to be accessed from LimitLevel of length 0"); } } inline int get_tail() { if (get_length()) { return orders->back(); } else { throw std::length_error("Tail attempted to be accessed from LimitLevel of length 0"); } } inline int get_length() { return orders->size(); } private: std::mutex mtx; // 每个LimitLevel的专属锁,保护自身的orders队列和quantity };重构_match_orders函数的锁逻辑
替换全局锁为LimitLevel专属锁,仅在访问全局共享结构时使用全局mutex,并最小化全局锁的持有时间:void _match_orders(Order *order, LimitLevel *best_value) { if (best_value == nullptr) { return; } // 锁定当前操作的LimitLevel:同LimitLevel串行,不同的并行 std::lock_guard<std::mutex> level_lock(best_value->get_mutex()); while (best_value->quantity > 0 && order->quantity > 0 && best_value->get_length() > 0) { int head_order_id = best_value->get_head(); Order* head_order = nullptr; // 第一步:访问全局orders哈希表,处理订单数量更新 { std::lock_guard<std::mutex> global_lock(mtx); auto it = orders.find(head_order_id); if (it == orders.end()) { // 订单已被删除,直接移除队列头部 best_value->pop_left(); continue; } head_order = it->second; // 计算交易数量 int trade_qty = std::min(order->quantity, head_order->quantity); // 更新订单和LimitLevel的数量 order->quantity -= trade_qty; head_order->quantity -= trade_qty; best_value->quantity -= trade_qty; } // 全局锁自动释放 // 第二步:处理订单完成或LimitLevel清空的收尾逻辑 { std::lock_guard<std::mutex> global_lock(mtx); if (order->quantity == 0 && orders.count(order->id)) { cancel(order->id); // 确保cancel内部也使用全局锁保护共享结构 } if (head_order->quantity == 0 && orders.count(head_order_id)) { _pop_limit(best_value); // _pop_limit需保证线程安全,操作bids/asks时用全局锁 orders.erase(head_order_id); } if (best_value->quantity == 0) { std::map<int, LimitLevel*> *order_tree = _get_side(!(order->is_bid)); if (order_tree->count(best_value->price)) { order_tree->erase(best_value->price); } } } // 全局锁自动释放 } }配套修改辅助函数
确保cancel、_pop_limit等修改全局共享结构的函数内部,都使用全局mutex(即原有的mtx)来保护操作,避免线程安全问题。
关键注意事项
- 锁顺序一致性:所有线程必须遵循「先获取LimitLevel锁,再获取全局锁」的顺序,避免死锁;
- 最小化锁持有时间:将全局锁的持有范围压缩到仅必要的共享结构访问操作,减少锁竞争;
- Order对象的线程安全:Order的
quantity修改必须在全局锁保护下,因为多个线程可能同时访问同一个Order实例; - 异常安全:使用
std::lock_guard自动管理锁的释放,避免因异常导致锁泄漏。
内容的提问来源于stack exchange,提问作者gmandonnan
相关产品推荐
相关产品推荐

