如何在C++中确保智能指针引用有效?监控线程安全访问任务
问题描述
我有一个指标收集线程,负责检查工作线程并上报指标。这些工作线程可能随时终止,如何确保调用工作线程的指标上报函数时,该工作线程尚未终止?
- 尝试过
unique_ptr,但无法防止use-after-free问题,且不希望MyMetricMonitor拥有每个任务的所有权。 - 考虑过
shared_ptr,但希望MyTask在任务完成后能立即被释放。 - 疑问:
weak_ptr是否是最佳方案?
示例代码:
#include <folly/Executor.h> #include <folly/executors/CPUThreadPoolExecutor.h> class MyTask { public: MyTask() : {} void run() { } }; class MyMetricMonitor { public: void addTaskToMonitor(std::unique_ptr<MyTask>& task) { tasks.push_back(task); } void monitor() { for (auto& task : tasks_) { task->getMetric(); } } private: std::vector<MyTask> tasks; }; int main() { MyMetricMonitor monitor; std::thread monitorThread(monitor); auto executor = std::make_shared<folly::CPUThreadPoolExecutor>( 2, std::make_shared<folly::NamedThreadFactory>("Worker")); // Submit a task that is a class instance folly::via(executor.get(), [] { std::unique_ptr<MyTask> task = std::make_unique<MyTask>(42); task.run(); monitor.addTaskToMonitor(task) }); monitorThread.join(); }
解决方案:
weak_ptr是适配需求的最优方案 核心思路是用shared_ptr管理任务的生命周期,监控线程持有weak_ptr弱引用,既不抢占所有权,又能安全判断任务是否存活。
具体实现步骤
- 任务生命周期由执行方掌控:
任务执行逻辑中用shared_ptr<MyTask>持有任务,任务完成后shared_ptr被释放,任务会立即销毁,满足“任务完成后立即释放”的需求。 - 监控线程持有弱引用:
MyMetricMonitor存储std::weak_ptr<MyTask>,不会增加引用计数,完全不干预任务的生命周期,符合“不拥有任务所有权”的要求。 - 安全访问指标:
在监控逻辑中,通过weak_ptr.lock()尝试获取shared_ptr:- 若返回非空,说明任务存活,可安全调用
getMetric(); - 若返回空,说明任务已销毁,直接跳过并清理过期引用。
- 若返回非空,说明任务存活,可安全调用
修正后的代码示例
#include <folly/Executor.h> #include <folly/executors/CPUThreadPoolExecutor.h> #include <memory> #include <vector> #include <thread> #include <mutex> class MyTask { public: MyTask(int val) : value_(val) {} void run() { // 任务执行逻辑 } // 模拟指标获取函数 int getMetric() const { return value_; } private: int value_; }; class MyMetricMonitor { public: void addTaskToMonitor(std::weak_ptr<MyTask> taskWeak) { std::lock_guard<std::mutex> lock(mtx_); tasks_.push_back(std::move(taskWeak)); } void monitor() { std::lock_guard<std::mutex> lock(mtx_); auto it = tasks_.begin(); while (it != tasks_.end()) { if (auto taskPtr = it->lock()) { // 安全获取并上报指标 int metric = taskPtr->getMetric(); it++; } else { // 清理已销毁任务的弱引用 it = tasks_.erase(it); } } } private: std::vector<std::weak_ptr<MyTask>> tasks_; std::mutex mtx_; // 多线程访问容器必须加锁,避免竞态 }; int main() { MyMetricMonitor monitor; // 监控线程示例:实际场景应为定时循环调用monitor std::thread monitorThread([&monitor]() { monitor.monitor(); }); auto executor = std::make_shared<folly::CPUThreadPoolExecutor>( 2, std::make_shared<folly::NamedThreadFactory>("Worker")); folly::via(executor.get(), [&monitor]() { auto task = std::make_shared<MyTask>(42); task->run(); monitor.addTaskToMonitor(task); // lambda结束后task被释放,任务立即销毁 }); executor->join(); monitorThread.join(); }
关键细节说明
- 线程安全:
MyMetricMonitor的容器被多线程读写,必须用互斥锁保护,否则会出现竞态条件导致崩溃。 - 过期引用清理:定期清理容器中的过期
weak_ptr,避免容器无限膨胀。 - 为什么排除其他方案:
unique_ptr是独占所有权,无法安全共享引用,极易出现悬空指针;shared_ptr会延长任务生命周期,无法满足“任务完成后立即释放”的需求。
内容的提问来源于stack exchange,提问作者user27594645
相关产品推荐
相关产品推荐

