多线程下解引用this与shared_ptr是否线程安全?代码安全性问询
问题
我实现了一个用mutex保证多线程读写的队列,代码里把this指针和shared_ptr类型的m_mulque传递给了读写线程。想咨询:解引用this指针和m_mulque是否线程安全?也就是下面的代码是否线程安全,运行时有没有未定义行为?
#include <mutex> #include <queue> struct multithreaded_queue { void push(const std::size_t i) { std::lock_guard lock(m_mutex); m_queue.push(i); }; void try_pop() { std::lock_guard lock(m_mutex); m_queue.pop(); }; private: std::queue<std::size_t> m_queue; mutable std::mutex m_mutex; };
class example { public: example() { m_mulque = std::make_shared<multithreaded_queue>(); }; void run() { auto writer = [this]() { for (std::size_t i = 0; i < 1000; i++) { m_mulque->push(i); }; }; auto reader = [this]() { for (std::size_t i = 0; i < 1000; i++) { m_mulque->try_pop(); }; }; std::thread writer1(writer); std::thread reader1(reader); std::thread reader2(reader); writer1.join(); reader1.join(); reader2.join(); }; private: std::shared_ptr<multithreaded_queue> m_mulque; };
int main(int argc, char* argv[]) { example ex; ex.run(); //Is this thread safe to call? };
回答
1. 解引用this指针的安全性
你的代码里,run()创建的lambda捕获了this,但主线程中的example对象ex是在栈上创建的,而且run()会等待所有子线程执行完毕才返回(调用了join())。这意味着子线程存活期间,ex肯定不会被销毁,所以解引用this是安全的,不会出现访问已销毁对象的问题。
2. 解引用m_mulque的安全性
m_mulque是std::shared_ptr类型,它的引用计数操作是原子的,本身具备线程安全性。再结合你的代码逻辑:
- 构造函数在主线程中完成了
m_mulque的初始化,之后子线程只读取m_mulque,没有任何修改操作,不存在多线程竞争修改m_mulque的场景; - 子线程通过
m_mulque调用队列的push/try_pop时,队列内部已经用std::mutex做了同步,这部分的线程安全是有保障的。
所以解引用m_mulque也是线程安全的。
3. 代码存在的未定义行为
虽然上面两部分没问题,但你的代码有个明显的隐患:multithreaded_queue::try_pop()没有检查队列是否为空就直接调用pop()。写线程只push了1000个元素,但两个读线程总共要执行2000次pop,必然会出现队列空的时候调用pop()的情况——而标准库的std::queue::pop()要求队列必须非空,否则行为属于未定义。
总结
- 解引用
this和m_mulque是线程安全的; - 代码整体存在未定义行为,原因是读线程可能在队列空时执行
pop()。你需要修改try_pop()函数,先检查队列状态:
修改后的try_pop()示例:
bool try_pop() { std::lock_guard lock(m_mutex); if (m_queue.empty()) { return false; // 队列空,返回操作失败 } m_queue.pop(); return true; };
同时读线程的循环逻辑也要调整,改成直到成功完成1000次pop:
auto reader = [this]() { std::size_t success_count = 0; while (success_count < 1000) { if (m_mulque->try_pop()) { success_count++; } // 可选:加短暂休眠避免空转占用CPU // std::this_thread::sleep_for(std::chrono::microseconds(1)); }; };
内容的提问来源于stack exchange,提问作者Vero
相关产品推荐
相关产品推荐

