news 2026/4/18 7:20:12

std::promise和std::future的用法

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
std::promise和std::future的用法

1、std::promise和std::future注意用来在线程间传递数据(不用手工同步来传递数据)。

2、在之前通过传递引用来传递数据,也能达到上述效果,但是需要手动同步,否则获取到不可预测的结果。

#include <iostream> #include <thread> void func(std::vector<int>& myVector,int& result) { auto it = std::max_element(myVector.begin(),myVector.end()); result = *it; } int main() { int result = 0; std::vector<int> myVector{1,2,3,6,7,8,99,12}; std::thread thread01(&func,std::ref(myVector),std::ref(result));//创建一个子线程,调用func,并把结果放引用参数中 thread01.join();//这里要手动同步,等待线程结束,才能下一句正确打印 std::cout << result << std::endl;//如果上面没有join,就会得到错误的结果。 return 0; }

3、一种更安全的线程间传递数据的方式,使用promise设定值,使用promise.get_future,关联futrue对象,通过future.get()来获取promise设定的值,而且future的get自动同步promise,如果promise的set未完成,会自动阻塞并等待,直到set完成。

//更好的传递数据的方式 #include <iostream> #include <thread> #include <vector> #include <chrono> #include <future> #include <algorithm> void func(std::vector<int>& myVector,std::promise<int>& pro) { std::this_thread::sleep_for(std::chrono::seconds(2));//模拟子线程执行需要2秒 auto it = std::max_element(myVector.begin(),myVector.end()); pro.set_value(*it);//子线程用来设定结果 } int main() { std::vector<int> myVector{1,3,6,7,8,2,4}; std::promise<int> pro;//创建一个promise对象 std::future<int> fut = pro.get_future();//创建一个future对象,并关联到promise std::thread thread01(&func,std::ref(myVector),std::ref(pro)); int result = fut.get();//会自动等待promise.set()结束 std::cout << result << std::endl; thread01.join(); return 0; }

4、在函数参数中,type&&表示参数可以接受一个右值引用对象,即参数可以接受一个临时对象,针对不可复制对象传递参数时,需要使用std::move方式传递,转移对象的所有权。type&表示接受一个引用对象,如果对象也是不可复制对象,需要使用std::ref()函数,将对象进行包装为左值引用对象。

#include <iostream> #include <future> #include <thread> #include <vector> #include <algorithm> #include <chrono> //线程函数 void find_max(const std::vector<int>& vec, std::promise<int>&& prom)//接受一个promise右值引用对象 //void find_max(const std::vector<int>& vec, std::promise<int>& prom)//接受一个promise引用对象 { std::this_thread::sleep_for(std::chrono::seconds(2)); try { auto it = std::max_element(vec.begin(), vec.end()); prom.set_value(*it);//设置结果 } catch (...) { prom.set_exception(std::current_exception());//设置异常结果 } } void findMin(std::vector<int>& myVector,int& result) { auto it = std::min_element(myVector.begin(), myVector.end()); result = *it; } int main() { std::vector<int> data{1,5,3,9,2}; std::promise<int> prom;//主线程创建一个promise对象 std::future<int> fut = prom.get_future();//主线程通过调用get_future()获得与promise对象相关联的future对象 std::thread worker(find_max, std::cref(data), std::move(prom));//创建一个子线程,传递数据和promise对象的右值引用 //std::thread worker(&find_max, std::ref(data), std::ref(prom));//传递promise的左值引用对象 int result = fut.get();//主线程通过调用future对象的get()方法获取结果,如果子线程未完成计算,主线程将会阻塞等待 std::cout << "Max element: " << result << std::endl; int result2 = 0; std::thread thread02(&findMin, std::ref(data), std::ref(result2)); thread02.join();//必须手动同步线程,等待线程完成后才能访问result2 std::cout << "Min element: " << result2 << std::endl; worker.join(); return 0; }

5、以上2种方式,都可以完成线程之间数据的传递,推荐使用promise和future的方式,这样自动线程同步;而且不仅提供了设定值,而且异常时,可以设定异常,promise.set_exception()来设定异常;而且当不需要阻塞时,可以使用wait_for() wait_until()来设定等待的时间,特别适用于生产者消费者模式,但注意的是promise只能set_value一次,future也只能get一次。

6、鉴于这种线程间通信传递数据方式,只适合于“单次、异步任务的结果传递”,不适合“持续生成和消费”的情形。

C++原生支持持续生成和消费者模式。如下:

#include <iostream> #include <thread> #include <queue> #include <mutex> #include <condition_variable> #include <chrono> // 简单的线程安全阻塞队列 template<typename T> class BlockingQueue { private: std::queue<T> queue_; std::mutex mtx_; std::condition_variable cv_producer_; // 通知生产者(队列不满) std::condition_variable cv_consumer_; // 通知消费者(队列不空) size_t max_size_; public: BlockingQueue(size_t max_size) : max_size_(max_size) {} // 生产者:放入数据。如果队列满了就阻塞等待 void push(T item) { std::unique_lock<std::mutex> lock(mtx_); cv_producer_.wait(lock, [this] { return queue_.size() < max_size_; }); queue_.push(std::move(item)); std::cout << "[生产] 生产了一个数据,队列大小: " << queue_.size() << std::endl; cv_consumer_.notify_one(); // 叫醒一个正在等待的消费者 } // 消费者:取出数据。如果队列空了就阻塞等待 T pop() { std::unique_lock<std::mutex> lock(mtx_); cv_consumer_.wait(lock, [this] { return !queue_.empty(); }); T item = std::move(queue_.front()); queue_.pop(); std::cout << "[消费] 消费了一个数据,队列剩余: " << queue_.size() << std::endl; cv_producer_.notify_one(); // 叫醒一个正在等待的生产者 return item; } }; // 使用示例 int main() { BlockingQueue<int> bq(5); // 容量为5的队列 // 生产者线程 std::thread producer([&bq] { for (int i = 0; i < 20; ++i) { bq.push(i); std::this_thread::sleep_for(std::chrono::milliseconds(50)); } }); // 消费者线程 std::thread consumer([&bq] { for (int i = 0; i < 20; ++i) { int val = bq.pop(); std::this_thread::sleep_for(std::chrono::milliseconds(150)); // 消费慢一点 } }); producer.join(); consumer.join(); return 0; }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 7:15:46

vLLM-v0.17.1实战教程:多LoRA动态切换支持个性化Agent服务

vLLM-v0.17.1实战教程&#xff1a;多LoRA动态切换支持个性化Agent服务 1. vLLM框架简介 vLLM是一个专为大型语言模型(LLM)设计的高性能推理和服务库&#xff0c;以其出色的吞吐量和易用性著称。这个项目最初由加州大学伯克利分校的天空计算实验室开发&#xff0c;现在已经发展…

作者头像 李华
网站建设 2026/4/18 7:13:33

手把手教你用Wan2.2-T2V-A5B:从文字到视频,5步搞定完整流程

手把手教你用Wan2.2-T2V-A5B&#xff1a;从文字到视频&#xff0c;5步搞定完整流程 1. 认识Wan2.2-T2V-A5B视频生成模型 Wan2.2-T2V-A5B是一款由通义万相开源的轻量级文本到视频生成模型&#xff0c;拥有50亿参数规模。虽然参数不算大&#xff0c;但它在速度和资源占用上表现…

作者头像 李华
网站建设 2026/4/18 7:11:28

从零构建基于Prometheus的DELL服务器硬件健康监控体系

1. 为什么需要DELL服务器硬件健康监控 作为运维工程师&#xff0c;我经历过太多次半夜被叫醒处理服务器硬件故障的情况。有一次凌晨3点&#xff0c;机房一台DELL R740的RAID卡突然故障&#xff0c;导致整个业务系统瘫痪。更糟的是&#xff0c;由于缺乏有效的硬件监控&#xff0…

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

指针的初步学习

一.指针的定义与解引用&#xff1a;定义&#xff1a;指针是用来存放变量地址的变量。int a 0;int *p &a;p指向a,p保存a的地址。int **q &p;二级指针解引用&#xff1a;通过指针访问它指向的变量通过q修改a的值&#xff1a;**q 1;通过q指针修改p的指向&#xff0c;指…

作者头像 李华
网站建设 2026/4/18 7:10:13

【Markdown编辑器使用】

Markdown编辑器使用欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚…

作者头像 李华