You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何优化多线程DataProcessor类以避免锁等待阻塞问题?

问题描述

我被这个问题困扰了一段时间。我有一个C++类,包含多个public函数,这些函数会被不同线程定期调用。该类的基本结构如下:

class DataProcessor
{
public:
    void process1(...);
    void process2(...);
    void process3(...);
    // etc...

    void update();
};

这是一个机器人应用,每个process函数用于处理不同传感器的数据,每当有新传感器数据传入时,就会在独立线程中调用对应的process函数。update函数运行在如下循环中:

while(true) {
   wait 1 second;
   dataProcessor.update();
}

目前我已确保各process函数可安全并发执行,但update函数不能在任何process函数执行期间调用。update函数执行速度极快,而部分process函数较为耗时,执行时间约0.1-0.2秒。

我尝试了两种安全执行策略,但都导致函数花费大量时间阻塞/等待互斥锁解锁:

  • 第一种策略:让每个process函数在执行前后分别锁定和解锁各自的mutex,update函数需持有所有mutex才能执行。但会出现如下问题:

    1. process1执行完成并解锁mutex 1
    2. update函数锁定mutex 1,等待mutex 2解锁
    3. process1再次被调用,需等待update函数解锁mutex 1
    4. update函数需完成执行才能解锁mutex 1,但它还在等待其他所有process函数执行完毕
      这导致process1实际上需要等待所有其他process函数完成,尽管process函数本应支持并发。
  • 第二种策略:使用计数器变量numberOfFunctionsRunning,每个process函数在开始时递增变量,结束时递减。update函数仅在变量为0时执行。这种方式下process函数可重新并发,但update函数因变量几乎无法归零而陷入永久等待。

请问有什么线程策略能确保process函数低等待运行,同时保证update函数定期执行?

(更新:我从回答和评论中得到了一些可行思路,本周会开始尝试,有结果后再更新!)


解决方案

采用准入控制+条件变量的方案,核心逻辑是:当update需要执行时,先禁止新的process启动,等待所有正在运行的process完成后执行update,执行完毕后恢复允许process正常并发。具体实现如下:

1. 类内新增控制成员

#include <mutex>
#include <condition_variable>

class DataProcessor
{
private:
    std::mutex mtx;
    std::condition_variable cv;
    int running_processes = 0;
    bool update_pending = false;  // 标记是否有update等待执行

public:
    void process1(...);
    void process2(...);
    void process3(...);
    void update();
};

2. 修改process函数的执行流程

每个process启动前先检查是否有update等待,若有则暂停等待,直到update完成;若无则正常启动并更新运行计数器:

void DataProcessor::process1(...)
{
    std::unique_lock<std::mutex> lock(mtx);
    // 等待update执行完成,允许新process启动
    cv.wait(lock, [this](){ return !update_pending; });
    
    running_processes++;
    lock.unlock();  // 释放锁,不影响其他process并发执行

    // --- 原process1的业务逻辑 ---
    // ...
    // --- 业务逻辑结束 ---

    lock.lock();
    running_processes--;
    // 通知update:当前process已完成
    cv.notify_one();
}

所有process函数都需要按此模板修改。

3. 修改update函数的执行流程

update先标记需要执行,等待所有正在运行的process结束后执行自身逻辑,最后清除标记并恢复process的准入:

void DataProcessor::update()
{
    std::unique_lock<std::mutex> lock(mtx);
    // 标记update待执行,禁止新process启动
    update_pending = true;
    // 等待所有正在运行的process全部结束
    cv.wait(lock, [this](){ return running_processes == 0; });

    // --- 原update的业务逻辑 ---
    // ... 此处执行速度极快,不会长时间占用锁
    // --- 业务逻辑结束 ---

    // 清除标记,允许新process启动
    update_pending = false;
    // 通知所有等待的process可以继续执行
    cv.notify_all();
}

方案优势

  • process低等待:只有当update请求存在时,新的process才会短暂等待,其余时间完全支持并发,不会出现跨process的阻塞问题。
  • update定期执行:每次update被调用时,都会快速等待现有process完成后执行,不会出现永久等待的情况。
  • 性能开销低:锁仅在控制逻辑阶段持有,业务逻辑执行期间不占用锁,对process的并发效率影响极小。

补充说明

如果使用C20及以上版本,也可以用std::barrier或std::latch简化控制逻辑,但上述方案基于C11即可实现,兼容性更强。


内容的提问来源于stack exchange,提问作者Eric Foote

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 18:12:43