在 C++ 中,std::thread
用于创建和管理线程,但它并不直接支持获取线程的返回结果。为了解决这个问题,我们可以使用 std::packaged_task
来辅助实现。std::packaged_task
是一个可调用对象包装器,它将一个函数封装起来,并可以与 std::future
结合使用来异步获取函数的结果。
使用步骤:
- 封装任务:将需要在线程中执行的函数传给
std::packaged_task
。 - 获取 future 对象:通过
std::packaged_task
的get_future()
方法获得一个std::future
对象。 - 启动线程:将
packaged_task
移交给std::thread
并启动线程。 - 获取结果:通过
future
对象的get()
方法获取线程执行的返回值。
#if 1 #define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<thread> #include<chrono> #include<future> int task(int number) { std::this_thread::sleep_for(std::chrono::seconds(5)); // 在线程函数内部既可以抛出异常,也可以正常返回结果 //throw std::exception("抛出异常!"); return 100 + number; } void test() { // 1. 封装任务 std::packaged_task<int(int)> current_task(task); // 2. 获取 future 对象 std::future<int> result = current_task.get_future(); // 3. 启动线程 // packaged_task 不支持拷贝,只支持移动 // std::move 将 current_task 转换为 右值引用,将其所有权转移给 std::thread // std::thread 接收到 current_task 的 所有权,并会在内部保存这个对象。 // std::thread t(std::move(current_task), 200); // std::ref 传递的是 current_task 的 引用,线程内操作的是原始对象。 // std::thread 不会接管 current_task 的生命周期,也不会负责销毁 current_task。 std::thread t(std::ref(current_task), 200); t.detach(); // 4. 获取结果 // 线程函数可能正确执行返回结果,也可能会抛出异常,需要使用 try 块来处理 try { int ret = result.get(); std::cout << "ret = " << ret << std::endl; } catch (const std::exception& ex) { std::cout << ex.what() << std::endl; } } int main() { test(); return EXIT_SUCCESS; } #endif
使用 get 函数获得结果会阻塞当前线程。如果我们不想阻塞在此处,可以使用 wait_for
或者 wait_until
来代替 get 函数。
#if 1 #define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<thread> #include<chrono> #include<future> int task(int number) { std::this_thread::sleep_for(std::chrono::seconds(5)); return 100 + number; } void test() { std::packaged_task<int(int)> current_task(task); std::future<int> result = current_task.get_future(); std::thread t(std::move(current_task), 200); while (true) { // 等待指定长度的时间,如果在该时间内子线程没有返回结果,则返回 timeout 状态,否则返回 ready 状态 // std::future_status status = result.wait_for(std::chrono::seconds(1)); std::future_status status = result.wait_until(std::chrono::steady_clock::now() + std::chrono::seconds(1)); if (status == std::future_status::ready) { std::cout << "线程结果是:" << result.get() << std::endl; break; } if (status == std::future_status::timeout) { std::cout << "任务正在执行中...做点别的事情" << std::endl; } } t.join(); } int main() { test(); return EXIT_SUCCESS; } #endif
std::future 只可以获得一次结果,不允许多次获得线程结果。如果需要多次获得线程的结果,可以使用 std::shared_future 来代替 std::future。
#if 1 #define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<thread> #include<chrono> #include<future> int task(int number) { std::this_thread::sleep_for(std::chrono::seconds(5)); return 100 + number; } void test() { std::packaged_task<int(int)> current_task(task); #if 0 std::future<int> result = current_task.get_future(); #else std::shared_future<int> result = current_task.get_future(); #endif std::thread t(std::move(current_task), 200); t.join(); result.get(); result.get(); } int main() { test(); return EXIT_SUCCESS; } #endif