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

如何在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弱引用,既不抢占所有权,又能安全判断任务是否存活。

具体实现步骤

  1. 任务生命周期由执行方掌控:
    任务执行逻辑中用shared_ptr<MyTask>持有任务,任务完成后shared_ptr被释放,任务会立即销毁,满足“任务完成后立即释放”的需求。
  2. 监控线程持有弱引用:
    MyMetricMonitor存储std::weak_ptr<MyTask>,不会增加引用计数,完全不干预任务的生命周期,符合“不拥有任务所有权”的要求。
  3. 安全访问指标:
    在监控逻辑中,通过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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 12:17:02