C++多线程编程(3):接收线程处理函数的返回值

文章首发于我的个人博客:欢迎大佬们来逛逛

文章目录

  • 处理带返回值的函数
    • async
    • packaged_task
    • promise

处理带返回值的函数

有三种方法:

  • async
  • packaged_task
  • promise

async

第一种方法是使用 async 函数。

步骤:

  1. 使用 async 创建线程处理函数
  2. 使用 .get() 获取返回值。

async函数具有两个属性

  • launch::async(默认):表示创建线程处理函数并且立刻执行
  • launch::defered:延期,当使用wait或者get的时候才会执行线程处理函数

async函数的返回值:std::future 类型,通过调用其 get 方法获取返回值

下面分别演示了普通函数类的成员函数以及 defered 的作用:

int value1(int num) {return num; 
}//类对象
class Foo {
public:Foo() {}int getValue(int num) {std::chrono::milliseconds duration(2000);std::this_thread::sleep_for(duration);return num * 2;}
};void testAsync() {//直接执行,默认是launch::asyncstd::future<int> res1 = std::async(value1, 100);std::cout << res1.get() << '\n';Foo m{};//类成员函数std::future<int> res2 = std::async(&Foo::getValue, &m, 200);std::cout << res2.get() << '\n';//不会立刻执行auto res3 = std::async(std::launch::deferred, &Foo::getValue, m, 400);//调用get,执行线程std::cout << res3.get() << '\n';
}

在这里插入图片描述


packaged_task

第二种方法是使用 packaged_task 方法

步骤:

  1. 使用 packaged_task 来包装线程处理函数
  2. 然后将这个包装好的函数加入到线程 thread 中,并且执行线程处理函数
  3. 最后使用这个 packaged_task 调用 get_future 来获取 future,然后调用 get 获取值。

package_task 函数包装语法:

//包装普通函数
std::packaged_task<返回类型(形参列表)> pack1(函数名称);//包装类的成员函数
std::packaged_task<返回类型(形参列表)> pack2(bind(函数地址,成员变量地址,placeholders占位符))//包装lambda表达式
std::packaged_task<int(int)> pack3([](形参列表){xxxxreturn xxx;
});

可以看到对于类的成员函数是相对比较复杂的。

void testPackaged_task() {//1. 普通函数的包装std::packaged_task<int(int)> pack1(value1);std::thread t1(std::ref(pack1),100);  //转换为&&t1.join();std::cout << pack1.get_future().get() << '\n';//2. 类中成员函数的包装Foo m{};std::packaged_task<int(int)> pack2(std::bind(&Foo::getValue, &m, std::placeholders::_1));std::thread t2(std::ref(pack2), 200);t2.join();std::cout << pack2.get_future().get() << '\n';//3. lambda表达式std::packaged_task<int(int)> pack3([](int num) {std::cout << "id: " << std::this_thread::get_id() << '\n';return num * 2;});std::thread t3(std::ref(pack3),300);t3.join();std::cout << pack3.get_future().get() << '\n';
}

在这里插入图片描述


promise

第三种方法是使用 promise 类型

步骤:

  1. 传递 promise 类型的变量到线程处理函数中。
  2. 我们正常执行线程处理函数即可,无需使用return语句,在操作完成后把需要的值 set_value 设置为promise 的值。
  3. 然后使用 thread 创建并且执行线程处理函数。
  4. 然后我们就可以在外部使用 .get_future 获取 future对象, 继而 .get 获取值。

这种方法的特点:

  • 无需显示设置 return 语句
  • 需要往线程处理函数添加一个额外的 promise 类型的参数。

例如这个是我们的线程处理函数,我们需要返回 num *3, 但是现在我们添加一个promise 类型的参数(注意是引用),然后直接 set_value 即可,然后再外部就可以直接访问这个值了。

void testThread(std::promise<int>& pms, int num) {std::cout << get_id() << '\n';pms.set_value(num * 3);
}
  • 返回线程处理函数的值:
std::promise<int> pms;
std::thread t1(testThread, std::ref(pms), 100);
t1.join();
auto num = pms.get_future().get();
std::cout << num << '\n';

这种方法也可以传递线程的值到另一个线程处理函数中:

有一个 testGetValueThread 线程函数,我们需要把刚才获取的 num*3 的值再传递进去,则可以在这个线程函数内调用 .get_future().get() 来传递参数。

下面是两种方法,这里使用了函数重载作为线程处理函数,需要使用static_cast来避免重载歧义。

通过static_cast消除重载函数的歧义

void testGetValueThread(std::promise<int>& pms) {std::cout << "获取值: " << pms.get_future().get() << '\n';
}
void testGetValueThread(int num) {std::cout << "获取值: " << num << '\n';
}...std::promise<int> pms2;pms2.set_value(99);//值传递到其他线程中//通过static_cast消除重载函数的歧义std::thread t2(static_cast<void(*)(std::promise<int>&)>(testGetValueThread), std::ref(pms2));t2.join();std::thread t3(static_cast<void(*)(int)>(testGetValueThread), num);t3.join();

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/194133.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++多线程编程(1):线程的创建方式

文章首发于我的个人博客&#xff1a;欢迎大佬们来逛逛 文章目录 进行与线程C中如何实现多线程创建线程的多种方式无参函数lambda表达式常成员函数not常成员引用函数智能指针仿函数类的普通成员函数综合测试 进行与线程 多线程是指多个线程并发执行的过程。 进程与线程的关系&…

QTableWidget——表格的合并与拆分

一、整体思路 表格的操作使用QTableView::setSpan可以实现表格的行和列的合并 表格拆分没有对应的处理函数 主要思路&#xff1a;对表格的属性、内容、拆分与合并的参数进行存储&#xff0c;在进行拆分时对表格内容进行重新创建&#xff08;不考虑效率问题&#xff09; 二、效…

C/C++ 语言 ‘ == ‘ 运算符仅适用于算数表达式

示例代码&#xff1a; #include <stdio.h>typedef struct {int a;int b; } TestStruct;int main(void) {TestStruct testA { 0 }, testB { 0 };if (testA testB) {printf("You can do this!\n");}return 0; }

HTTP1.0协议详解

前言主要特点存在的不足与HTTP1.1的区别在Java中应用HTTP1.0协议知识拓展 前言 HTTP是由蒂姆伯纳斯李&#xff08;Tim Berners-Lee&#xff09;爵士创造的。他在1989年提出了一个构想&#xff0c;借助多文档之间相互关联形成的超文本&#xff08;HyperText&#xff09;&#x…

【心得】PHP的文件上传个人笔记

目录 1 php的文件上传绕过 黑名单绕过 2 php文件上传的00截断 3 iconv字符转换异常后造成了字符截断 4 文件后缀是白名单的时候的绕过 web服务器的解析漏洞绕过 5.高级文件上传绕过 1 .htaccess nginx.htaccess 2 服务端内容检测 3 配合伪协议来绕过 4.配合日志包含绕…

智能电力监测系统

智能电力监测系统是一种先进的电力监控技术&#xff0c;它结合了互联网、物联网、大数据、人工智能等先进技术&#xff0c;对电力系统的运行状态进行实时监测和分析。以下是智能电力监测系统的主要功能和优势&#xff1a; 一、主要功能&#xff1a; 实时数据监测&#xff1a;智…

时间序列与 statsmodels:预测所需的基本概念(2)

时间序列与 statsmodels&#xff1a;预测所需的基本概念&#xff08;2&#xff09; 维托米尔约万诺维奇 跟随 出版于 走向发展 4 分钟阅读 2022 年 1 月 31 日 8 一、说明 在使时间序列平稳后&#xff0c;在本博客中我们应用 SARIMAX 预测并进行深入解释。 二、关于平稳性 …

接口自动化测试中解决接口间数据依赖

在实际的测试工作中&#xff0c;在做接口自动化测试时往往会遇到接口间数据依赖问题&#xff0c;即API_03的请求参数来源于API_02的响应数据&#xff0c;API_02的请求参数又来源于API_01的响应数据。 因此通过自动化方式测试API_03接口时&#xff0c;需要预先请求API_02接口&a…

轻松掌控财务,分析账户花销,明细记录支出情况

随着科技的发展&#xff0c;我们的生活变得越来越智能化。然而&#xff0c;对于许多忙碌的现代人来说&#xff0c;管理财务可能是一件令人头疼的事情。复杂的账单、花销、收入&#xff0c;这些可能会让你感到无从下手。但现在&#xff0c;我们有一个全新的解决方案——一款全新…

[qemu逃逸] XNUCA2019-vexx

前言 这题没有去符合, 题目本身不算难. 用户名: root 密码: goodluck 设备逆向 题目没有去符合, 所以其实没啥好讲了, 就列一些笔者认为关键的地方 这里的定义了两块 mmio 内存区. 然后看下设备实例结构体: 可以看到 QEMUTimer, 所以多半就是劫持 dma_timer 了. 漏洞点在…

传输层协议-TCP协议

目录 TCP协议格式理解可靠性序号与确认序号16位窗口大小六个标志位连接管理机制三次握手四次挥手 确认应答机制&#xff08;ACK&#xff09;超时空重传机制流量控制滑动窗口拥塞控制延迟应答捎带应答面向字节流粘包问题TCP异常情况TCP小结基于TCP应用层协议TCP/UDP对比用UDP实现…

【STL】:反向迭代器

朋友们、伙计们&#xff0c;我们又见面了&#xff0c;本期来给大家解读一下有关反向迭代器的模拟实现&#xff0c;如果看完之后对你有一定的启发&#xff0c;那么请留下你的三连&#xff0c;祝大家心想事成&#xff01; C 语 言 专 栏&#xff1a;C语言&#xff1a;从入门到精通…