news 2026/7/24 13:24:04

std::thread::join() 和 std::future::get()的调用顺序分析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
std::thread::join() 和 std::future::get()的调用顺序分析

一 概述

std::thread::join() 和 std::future::get() 都是阻塞调用,但它们在调用顺序和线程管理上有重要区别。

二 基本区别

1 std::thread::join()
std::thread t([](){ /* 任务 */ });
t.join(); // 等待线程结束,但不获取返回值

目的:等待线程执行完成。
返回值:无(void)。
线程状态:线程结束后,线程对象变为不可 join。
调用次数:只能调用一次。

2 std::future::get()


std::future<int> fut = std::async(std::launch::async, [](){ return 42; });
int result = fut.get(); // 等待并获取结果

目的:等待异步操作完成并获取结果。
返回值:返回异步操作的结果。
状态:get() 后 future 变为无效。
调用次数:只能调用一次。

三 调用顺序对比

1 示例1:只有 join()


#include <iostream>
#include <thread>
#include <chrono>

void worker(int id) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
std::cout << "Worker " << id << " finished\n";
}

int main() {
std::thread t1(worker, 1);
std::thread t2(worker, 2);

// 顺序等待
t1.join(); // 阻塞,等待 t1 完成
t2.join(); // 阻塞,等待 t2 完成

std::cout << "All threads joined\n";
return 0;
}

2 示例2:只有 get()


#include <iostream>
#include <future>
#include <chrono>

int task(int x) {
std::this_thread::sleep_for(std::chrono::milliseconds(x * 100));
return x * 10;
}

int main() {
auto fut1 = std::async(std::launch::async, task, 3);
auto fut2 = std::async(std::launch::async, task, 1);

// get() 会阻塞直到结果可用
int result2 = fut2.get(); // 先获取快的任务
std::cout << "Result2: " << result2 << "\n";

int result1 = fut1.get(); // 再获取慢的任务
std::cout << "Result1: " << result1 << "\n";

return 0;
}

四 同时使用 join() 和 get()

1 先 join() 再 get()(通常不必要)


#include <iostream>
#include <thread>
#include <future>
#include <chrono>

int main() {
std::promise<int> prom;
std::future<int> fut = prom.get_future();

std::thread t([&prom]() {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
prom.set_value(42);
});

t.join(); // 等待线程完成
int value = fut.get(); // 立即获取结果(因为线程已结束)

std::cout << "Value: " << value << "\n";
return 0;
}

2 先 get() 再 join()(更常见)


#include <iostream>
#include <thread>
#include <future>
#include <chrono>

int main() {
std::promise<int> prom;
std::future<int> fut = prom.get_future();

std::thread t([&prom]() {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
prom.set_value(42);
});

int value = fut.get(); // 阻塞直到结果可用
t.join(); // 立即返回(因为线程已结束)

std::cout << "Value: " << value << "\n";
return 0;
}

五 最佳实践和推荐顺序

推荐:先 get() 再 join()


std::promise<T> prom;
std::future<T> fut = prom.get_future();
std::thread t(worker_function, std::ref(prom));

// 先获取结果(这会等待线程完成计算)
T result = fut.get();

// 再清理线程资源
t.join();

优点:

get() 已经隐含等待线程完成的功能。
join() 调用时线程已经结束,会立即返回。
代码逻辑更清晰。

六 实际应用示例

1 多任务并行计算


#include <iostream>
#include <vector>
#include <thread>
#include <future>
#include <numeric>
#include <chrono>

int process_chunk(const std::vector<int>& data, int start, int end) {
int sum = 0;
for (int i = start; i < end; ++i) {
sum += data[i];
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
return sum;
}

int main() {
std::vector<int> data(1000);
std::iota(data.begin(), data.end(), 1);

// 创建多个future
std::vector<std::future<int>> futures;

// 启动多个线程
int chunk_size = data.size() / 4;
for (int i = 0; i < 4; ++i) {
int start = i * chunk_size;
int end = (i == 3) ? data.size() : (i + 1) * chunk_size;

futures.push_back(
std::async(std::launch::async, process_chunk,
std::cref(data), start, end)
);
}

// 按任意顺序获取结果
// get() 会按完成顺序阻塞
std::vector<int> results;
for (auto& fut : futures) {
results.push_back(fut.get()); // 等待并获取结果
}

int total = std::accumulate(results.begin(), results.end(), 0);
std::cout << "Total sum: " << total << "\n";

// 注意:使用 std::async 时,不需要显式 join
// future 析构时会自动等待

return 0;
}

2 手动管理线程和 promise


#include <iostream>
#include <thread>
#include <future>
#include <vector>
#include <stdexcept>

class ThreadManager {
std::vector<std::thread> threads;
std::vector<std::future<int>> futures;

public:
void add_task(std::function<int()> task) {
std::promise<int> prom;
futures.push_back(prom.get_future());

threads.emplace_back([prom = std::move(prom), task]() mutable {
try {
int result = task();
prom.set_value(result);
} catch (...) {
prom.set_exception(std::current_exception());
}
});
}

std::vector<int> wait_all() {
std::vector<int> results;

// 先获取所有结果
for (auto& fut : futures) {
try {
results.push_back(fut.get());
} catch (const std::exception& e) {
std::cout << "Task failed: " << e.what() << "\n";
results.push_back(-1);
}
}

// 再清理所有线程
for (auto& t : threads) {
if (t.joinable()) {
t.join();
}
}

return results;
}

~ThreadManager() {
// 确保所有线程都已结束
for (auto& t : threads) {
if (t.joinable()) {
t.join();
}
}
}
};

int main() {
ThreadManager manager;

manager.add_task([]() { return 1; });
manager.add_task([]() { return 2; });
manager.add_task([]() {
throw std::runtime_error("Task failed!");
return 3;
});

auto results = manager.wait_all();

for (auto r : results) {
std::cout << "Result: " << r << "\n";
}

return 0;
}

七 重要注意事项

1 异常处理


std::future<int> fut = std::async([]() {
throw std::runtime_error("Oops!");
return 42;
});

try {
int value = fut.get(); // 会抛出异常
} catch (const std::exception& e) {
std::cout << "Exception: " << e.what() << "\n";
}

2 避免死锁


// 错误示例:自死锁
std::promise<int> prom;
std::future<int> fut = prom.get_future();

std::thread t([&fut, &prom]() {
// 这个线程在等待自己设置结果!
// int value = fut.get(); // 死锁!
prom.set_value(42);
});

t.join();

3 std::async 的特殊行为


// 使用 std::launch::deferred 策略
auto fut = std::async(std::launch::deferred, []() {
return 42;
});

// get() 会在调用线程中同步执行任务
int value = fut.get(); // 此时才执行任务

4 超时等待


std::future<int> fut = std::async([]() {
std::this_thread::sleep_for(std::chrono::seconds(2));
return 42;
});

// 使用 wait_for 非阻塞等待
auto status = fut.wait_for(std::chrono::seconds(1));
if (status == std::future_status::ready) {
int value = fut.get();
} else {
std::cout << "Task not ready yet\n";
}

八 总结

1 对于纯线程管理:只需 join()。
2 对于需要结果的异步任务:使用 std::future 和 get()。
3 当同时使用 thread 和 future:推荐先 get() 再 join()。这种顺序选择主要是为了代码清晰和避免重复等待,因为 get() 已经隐含了等待操作完成的语义。
4 使用 std::async:只需 get(),不需要显式 join()。

九 通用模式


// 如果需要结果
std::future<T> fut = std::async(task);
T result = fut.get(); // 等待并获取结果

// 如果只是执行后台任务
std::thread t(task);
t.join(); // 等待完成

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/24 10:45:11

揭秘Open-AutoGLM集成难题:5大关键步骤彻底打通AI自动化 pipeline

第一章&#xff1a;Open-AutoGLM集成的核心挑战与价值在构建现代化智能系统的过程中&#xff0c;将大语言模型&#xff08;LLM&#xff09;如 Open-AutoGLM 与现有工程架构深度融合&#xff0c;成为提升自动化能力的关键路径。然而&#xff0c;这种集成不仅带来了性能和功能上的…

作者头像 李华
网站建设 2026/7/23 12:14:16

动态调控的博弈——台式机与笔记本处理器功耗管理机制差异

如果说核心设计逻辑是处理器功耗差异的“根”&#xff0c;技术实现路径是“茎”&#xff0c;那么动态功耗管理机制就是“叶”&#xff0c;它直接决定了处理器在实际运行过程中如何响应不同负载&#xff0c;实现性能与功耗的实时平衡。台式机与笔记本处理器的动态功耗管理机制&a…

作者头像 李华
网站建设 2026/7/16 15:19:18

从零开始构建RAG系统:大模型检索增强生成实战指南

文章详细介绍了RAG&#xff08;检索增强生成&#xff09;技术&#xff0c;解释了其如何解决大模型知识时效性有限、无法访问私有数据、可解释性差等问题。从概念、应用场景、行业案例到实际构建流程进行全面讲解&#xff0c;提供完整代码示例&#xff0c;帮助个人和小团队低成本…

作者头像 李华
网站建设 2026/7/22 0:55:59

Open-AutoGLM性能翻倍秘籍(仅限内部使用的优化参数首次公开)

第一章&#xff1a;Open-AutoGLM运行的慢在部署和使用 Open-AutoGLM 模型时&#xff0c;用户普遍反馈其推理速度较慢&#xff0c;影响实际应用场景中的响应效率。该问题通常由模型结构复杂、硬件资源不足或推理框架未优化等多方面因素共同导致。模型推理性能瓶颈分析 Open-Auto…

作者头像 李华