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


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

    处理带返回值的函数

    有三种方法:

    • 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::async
    	std::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';
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    在这里插入图片描述


    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([](形参列表){
    		xxxx
    		return xxx;
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

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

    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';
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    在这里插入图片描述


    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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 返回线程处理函数的值:
    std::promise<int> pms;
    std::thread t1(testThread, std::ref(pms), 100);
    t1.join();
    auto num = pms.get_future().get();
    std::cout << num << '\n';
    
    • 1
    • 2
    • 3
    • 4
    • 5

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

    有一个 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();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    在这里插入图片描述

  • 相关阅读:
    风控安全产品系统设计的一些思考
    避免数据泄露风险!NineData提供SQL开发规范和用户访问量管理
    Mac上brew切换国内源【极简方案】
    软件设计模式白话文系列(五)建造者模式
    【教3妹学编程-算法题】最大单词长度乘积
    redis中数据类型命令整理
    文件夹高效改名,批量设置文件夹仅显示编号无名称的方法“
    带宽优化新思路:RoCE网卡聚合实现X2增长
    43Nginx+Tomcat负载均衡与动静分离
    Java手写红黑树应用拓展案例
  • 原文地址:https://blog.csdn.net/jj6666djdbbd/article/details/134490840