复用std::unique_lock而非新建实例的代码正确性验证
关于std::unique_lock复用的正确性探讨
以下是一种常规的std::unique_lock使用方式:
while (!m_exit || !m_queueOfTasks.empty()) { std::unique_lock<std::mutex> ul(m_mutex); std::cout << "Thread " << std::this_thread::get_id() << " is ready" << std::endl; m_cond.wait(ul, [this]() {return !m_queueOfTasks.empty(); }); std::function<void()> work(std::move(m_queueOfTasks.front())); m_queueOfTasks.pop_front(); std::cout << "Thread " << std::this_thread::get_id() << " got a new task" << std::endl; ul.unlock(); work(); }
我们可以尝试复用std::unique_lock而非每次循环都创建新实例。根据其构造函数说明:
以m作为关联互斥量构造unique_lock,同时调用m.lock()锁定关联互斥量。
这意味着需要先创建未锁定的实例再手动加锁,于是编写了如下代码,请问该代码是否正确,是否有遗漏?
std::unique_lock<std::mutex> ul(m_mutex, std::defer_lock); while (!m_exit || !m_queueOfTasks.empty()) { ul.lock(); std::cout << "Thread " << std::this_thread::get_id() << " is ready" << std::endl; m_cond.wait(ul, [this]() {return !m_queueOfTasks.empty(); }); std::function<void()> work(std::move(m_queueOfTasks.front())); m_queueOfTasks.pop_front(); std::cout << "Thread " << std::this_thread::get_id() << " got a new task" << std::endl; ul.unlock(); work(); }
代码正确性分析
这段复用std::unique_lock的代码逻辑正确,无功能遗漏,和原代码行为完全等价,关键细节验证如下:
锁状态管理
- 初始化时通过
std::defer_lock构造未锁定的unique_lock,每次循环开头调用ul.lock()正确获取互斥量,符合预期。 m_cond.wait(ul, ...)的行为和原代码一致:等待前自动释放锁,满足条件后重新获取锁,这是unique_lock配合条件变量的标准逻辑,复用实例不影响此行为。- 任务取出后调用
ul.unlock()释放锁,之后执行work(),解锁时机和原代码完全匹配,不会导致锁持有时间过长。
- 初始化时通过
潜在的竞态问题(非复用导致)
- 循环开头的
!m_exit || !m_queueOfTasks.empty()检查是在无锁状态下进行的,这和原代码存在同样的竞态风险:如果其他线程同时修改m_exit或任务队列,可能导致判断结果不准确。但这属于原代码本身的逻辑问题,并非复用unique_lock带来的新问题。
- 循环开头的
复用的安全性
- 每次循环结束时
ul都处于未锁定状态(最后执行了ul.unlock()),下一次循环调用ul.lock()是合法操作,不存在重复加锁的风险。只要确保每次循环都正确解锁,复用unique_lock就是安全的。
- 每次循环结束时
内容的提问来源于stack exchange,提问作者amplifier
相关产品推荐
相关产品推荐

