编程学习
🔐C++多线程编程(3):接收线程处理函数的返回值
00 分钟
2023-7-12
2023-11-25
type
status
date
slug
summary
tags
category
icon
password
Email
文章首发于我的个人博客:欢迎大佬们来逛逛
C++多线程编程项目源码:Github地址

处理带返回值的函数

有三种方法:
  • async
  • packaged_task
  • promise

async

第一种方法是使用 async 函数。
步骤:
  1. 使用 async 创建线程处理函数
  1. 使用 .get() 获取返回值。
async函数具有两个属性
  • launch::async(默认):表示创建线程处理函数并且立刻执行
  • launch::defered:延期,当使用wait或者get的时候才会执行线程处理函数
async函数的返回值:std::future 类型,通过调用其 get 方法获取返回值
下面分别演示了普通函数类的成员函数以及 defered 的作用:
notion image

packaged_task

第二种方法是使用 packaged_task 方法
步骤:
  1. 使用 packaged_task 来包装线程处理函数
  1. 然后将这个包装好的函数加入到线程 thread 中,并且执行线程处理函数
  1. 最后使用这个 packaged_task 调用 get_future 来获取 future,然后调用 get 获取值。
package_task 函数包装语法:
要点:
  • packaged_task 非常适合用于异步执行任务的时候。
  • 它有点类似于std::function 函数包装器,都是把一个函数封装起来。
notion image
std::packaged_task 通常于 std::future 一起使用,std::future 是获取线程处理函数的返回值的方式。
看示例代码:
  • 使用 std::packaged_task 包装函数调用
  • task.get_future来获取此任务的std::future对象,以便往后任何时候获取线程处理的返回值。
  • std::packaged_task 是可移动的,但是不可拷贝。
  • std::future.wait 方法可以等待线程执行完毕。
  • 使用 .get 获取值。

与std::function的区别

std::packaged_task的优势:
  1. 任务封装: std::packaged_task 主要用于将函数或可调用对象封装为一个可异步执行的任务。
  1. 与 std::future 结合使用: std::packaged_task 通常与 std::future 结合使用,通过调用 get_future() 获取一个与任务相关联的 std::future 对象,以便获取任务执行的结果。
  1. 可移动但不可拷贝: std::packaged_task 对象是可移动但不可拷贝的,因为它们通常与异步执行相关,而拷贝可能导致不确定的行为。
  1. 通常用于异步任务: 适用于需要在其他线程中执行的任务,通过异步执行来达到并发的效果。
std::function 的优势:
  1. 可调用对象容器: std::function 是一个通用的可调用对象容器,可以包装函数、函数对象、Lambda 表达式等,提供了一种统一的方式来操作这些可调用对象。
  1. 类型擦除: std::function 使用类型擦除的技术,允许将不同类型的可调用对象存储在同一个对象中。
  1. 可拷贝: std::function 对象是可拷贝的,因为它们的设计目的是为了提供通用性和灵活性。
  1. 不涉及异步: 通常用于需要在同一线程中调用的场景,不涉及异步任务的执行。
注意 std::function 是不涉及异步操作的,因此如果想要在线程处理后获取值,必须在同一线程中获取,result必须随时接收此返回值,而 std::future可以在任意时刻 .get 获取其值。

promise

第三种方法是使用 promise 类型
步骤:
  1. 传递 promise 类型的变量到线程处理函数中。
  1. 我们正常执行线程处理函数即可,无需使用return语句,在操作完成后把需要的值 set_value 设置为promise 的值。
  1. 然后使用 thread 创建并且执行线程处理函数。
  1. 然后我们就可以在外部使用 .get_future 获取 future对象, 继而 .get 获取值。
这种方法的特点:
  • 无需显示设置 return 语句
  • 需要往线程处理函数添加一个额外的 promise 类型的参数。
例如这个是我们的线程处理函数,我们需要返回 num *3, 但是现在我们添加一个promise 类型的参数(注意是引用),然后直接 set_value 即可,然后再外部就可以直接访问这个值了。
  • 返回线程处理函数的值:
这种方法也可以传递线程的值到另一个线程处理函数中:
有一个 testGetValueThread 线程函数,我们需要把刚才获取的 num*3 的值再传递进去,则可以在这个线程函数内调用 .get_future().get() 来传递参数。
下面是两种方法,这里使用了函数重载作为线程处理函数,需要使用static_cast来避免重载歧义。
通过static_cast消除重载函数的歧义
 
notion image

评论
  • Twikoo
  • Valine