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

C++使用std::condition_variable实现相机优先级锁的线程安全问题求助

相机优先级访问控制实现问题排查与修复

核心问题定位

  • 重复加锁触发未定义行为:std::condition_variable_any的wait()接口执行谓词时,当前线程已经持有cameraMut互斥锁,原实现中isLockable函数内部再次对同一个非递归互斥锁加锁,属于非法操作,是段错误、double free的主要诱因。
  • 回调执行时持有锁导致死锁/UB:lock()函数在持有cameraMut的状态下调用抢占回调mUnlockCameraHandler,而回调中通常会调用unlock(),unlock()内部又会申请同一个互斥锁,二次加锁非递归锁直接触发未定义行为。
  • 状态竞争问题:抢占回调执行完成后,unlock()已经修改了currentPriority、mLockable等共享状态,lock()后续执行逻辑依然基于旧状态判断,会导致锁状态错乱,多个线程可能同时认为自己持有相机资源。
  • 最小复现代码中isLockable函数直接访问了未定义的局部变量mLock,属于低级笔误,会直接访问非法内存。

修复方案

  • 拆分isLockable接口:内部谓词版本不加锁,仅在持有锁的场景下调用;单独实现对外的公开查询版本,内部自行加锁,避免重复加锁问题。
  • 调用抢占回调前先释放互斥锁,回调执行完成后再重新加锁判断状态,避免二次加锁问题,同时处理回调执行期间状态被其他线程修改的情况。
  • 用lambda作为wait的谓词,逻辑更直观,减少不必要的调用开销。
  • 依赖std::unique_lock的RAII特性自动管理锁的释放,不需要手动调用unlock,避免漏解锁问题。
  • 解锁时清空回调对象,避免悬空引用风险。

修复后完整代码

#include <condition_variable>
#include <mutex>
#include <thread>
#include <functional>
#include <limits>
#include <memory>

typedef std::function<void()> UnlockCameraHandler;

class Camera {
    const int MAX_PRIORITY = std::numeric_limits<int>::max();
    int currentPriority = MAX_PRIORITY;
    bool mLockable = true;
    UnlockCameraHandler mUnlockCameraHandler;
    std::condition_variable_any cameraLock;
    std::mutex cameraMut;

    // 内部私有谓词,仅在持有cameraMut的前提下调用
    bool isLockableUnlocked(int priority) const {
        return priority < currentPriority;
    }

public:
    // 对外公开的查询接口
    bool isLockable(int priority) {
        std::unique_lock<std::mutex> mLock(cameraMut);
        return isLockableUnlocked(priority);
    }

    void unlock() {
        std::unique_lock<std::mutex> mLock(cameraMut);
        if (mLockable) {
            return;
        }
        currentPriority = MAX_PRIORITY;
        mUnlockCameraHandler = nullptr;
        mLockable = true;
        cameraLock.notify_all();
    }

    bool lock(int priority, UnlockCameraHandler handler) {
        if (priority < 0) {
            return false;
        }
        std::unique_lock<std::mutex> mLock(cameraMut);

        // 优先级更高,需要抢占当前持有者
        if (isLockableUnlocked(priority) && mUnlockCameraHandler) {
            auto tmpHandler = mUnlockCameraHandler;
            // 先解锁再调用回调,避免二次加锁
            mLock.unlock();
            tmpHandler();
            // 回调执行完重新加锁
            mLock.lock();
        }

        // 等待资源可用
        cameraLock.wait(mLock, [this, priority](){
            return isLockableUnlocked(priority) && mLockable;
        });

        // 更新锁状态
        mUnlockCameraHandler = std::move(handler);
        currentPriority = priority;
        mLockable = false;
        return true;
    }
};

// 测试代码
std::shared_ptr<Camera> camera = std::make_shared<Camera>();

void unlock_handler() {
    camera->unlock();
}

int main() {
    std::thread high{[&]() {
        camera->lock(50, std::bind(&unlock_handler));
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
        camera->unlock();
    }};

    std::thread low{[&]() {
        camera->lock(100, std::bind(&unlock_handler));
        camera->unlock();
    }};

    std::thread mid{[&]() {
        camera->lock(75, std::bind(&unlock_handler));
        std::this_thread::sleep_for(std::chrono::milliseconds(5));
        camera->unlock();
    }};
    
    high.join();
    low.join();
    mid.join();
    return 0;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 13:15:04