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
相关产品推荐
相关产品推荐

