C++技术问询:如何创建带指针参数的线程及实现自动join线程
C++中带指针参数的线程创建与自动join实现
我来帮你搞定这两个C++线程的问题,先从创建带指针/引用参数的线程说起,再帮你完善自动join的代码实现~
一、创建调用需指针/引用参数方法的线程
如果是类的成员函数,创建std::thread的时候得注意两个点:
- 必须把对象的指针(或者引用)作为第一个参数传进去,告诉线程要调用哪个对象的成员函数
- 如果参数是引用类型(就像你代码里的
atomic_bool&),得用std::ref()包装一下,不然线程构造的时候会把参数拷贝一份;要是参数是指针的话,直接传指针地址就行
举个实际的例子,对应你写的Foo::work方法(引用参数),创建线程的代码可以这么写:
Foo foo; std::atomic_bool isWorking(false); // 传foo的指针,再用std::ref包装isWorking确保是引用传递 std::thread t(&Foo::work, &foo, std::ref(isWorking));
要是你把work改成指针参数(比如void work(std::atomic_bool* isWorking)),那调用就更直接了:
std::thread t(&Foo::work, &foo, &isWorking);
二、实现工作完成后自动join的线程
你想要线程干完活自动join,最优雅的方式是用**RAII(资源获取即初始化)**思想封装一个线程类——简单说就是让这个线程对象在生命周期结束的时候,自动调用join(),完全不用你手动管什么时候调用。当然也可以用std::async配合std::future,但RAII的方式更灵活可控。
下面是完善后的完整代码,包含了RAII线程封装和你的Foo类实现:
#include <iostream> #include <thread> #include <chrono> #include <atomic> #include <stdexcept> // RAII线程包装类:对象销毁时自动join线程 class AutoJoinThread { private: std::thread t_; public: // 构造函数,支持接收任意可调用对象和参数 template<typename Func, typename... Args> explicit AutoJoinThread(Func&& func, Args&&... args) : t_(std::forward<Func>(func), std::forward<Args>(args)...) {} // 线程不能拷贝,所以禁用拷贝构造和赋值 AutoJoinThread(const AutoJoinThread&) = delete; AutoJoinThread& operator=(const AutoJoinThread&) = delete; // 支持移动语义 AutoJoinThread(AutoJoinThread&&) noexcept = default; AutoJoinThread& operator=(AutoJoinThread&&) noexcept = default; // 析构函数:自动检查线程是否可join,是的话就调用join ~AutoJoinThread() { if (t_.joinable()) { t_.join(); } } // 可选:暴露原始线程的接口,方便扩展 std::thread& get() { return t_; } }; class Foo { public: void work(std::atomic_bool& isWorking); }; void Foo::work(std::atomic_bool& isWorking) { int myVar = 0; while (myVar != 5) { std::cout << "工作中... 计数: " << myVar + 1 << "\n"; myVar++; std::this_thread::sleep_for(std::chrono::seconds(1)); } isWorking = true; std::cout << "工作完成!\n"; } int main() { Foo foo; std::atomic_bool isWorking(false); // 创建自动join的线程,不用手动调用join AutoJoinThread worker(&Foo::work, &foo, std::ref(isWorking)); // 主线程可以并行做其他事情 std::cout << "主线程处理其他任务...\n"; while (!isWorking) { std::this_thread::sleep_for(std::chrono::milliseconds(500)); } std::cout << "主线程检测到工作已完成!\n"; // worker对象生命周期结束时,析构函数会自动调用join return 0; }
代码说明:
- AutoJoinThread类:核心就是利用RAII,在对象销毁时自动处理线程的join,避免了手动管理的麻烦,也防止了线程未join导致的程序异常退出。
- 线程参数传递:因为
Foo::work是成员函数,所以第一个参数传&foo(对象指针),然后用std::ref(isWorking)传递引用,确保线程里操作的是同一个atomic_bool对象,不会出现拷贝后的独立变量。 - 主线程逻辑:主线程可以同时执行其他任务,通过
isWorking这个原子变量监听工作线程的完成状态,保证线程安全。
另外,如果你不想封装类,也可以用std::async配合std::future来实现自动等待——std::async创建的线程,当返回的future对象销毁时,会自动等待线程完成:
int main() { Foo foo; std::atomic_bool isWorking(false); // 用std::async创建异步任务,future销毁时自动等待线程完成 auto future = std::async(std::launch::async, &Foo::work, &foo, std::ref(isWorking)); std::cout << "主线程处理其他任务...\n"; while (!isWorking) { std::this_thread::sleep_for(std::chrono::milliseconds(500)); } std::cout << "主线程检测到工作已完成!\n"; return 0; }
这种方式更简洁,但std::async的调度策略可能受系统影响,如果你需要更直接的线程控制权,RAII封装的方式会更合适。
内容的提问来源于stack exchange,提问作者Salin
相关产品推荐
相关产品推荐

