首页 > C++ 多线程:future 异步访问类(线程之间安全便捷的数据共享)

C++ 多线程:future 异步访问类(线程之间安全便捷的数据共享)

文章目录

      • future前言
      • future描述
      • future类成员使用
      • 总结

future前言

首先查看如下代码

#include 
#include 
#include 
#include using namespace std;void fun1(int n,int &x) { int res = 1;for (int i = n; i>1; --i) { res *=i;}cout << "Result is " << res << endl;//输出最终的结果并赋值给共享变量xx = res;
}int main()
{ int x;std::thread t1(fun1,4,std::ref(x));t1.join();return 0;
}

这段代码仅拥有一个线程,但是可以很明显得看到主线程和t1线程想要共享同一个变量x,同时x是需要在t1线程进行赋值将结果返回给主线程,由主线程来获取。这样的线程之间的变量传递,我们所知道的传递方式只能是传递引用。同时仅仅传递引用是不够的,因为线程之间的变量共享需要对变量进行保护才能安全有效共享,这样就需要引入mutex互斥变量和condition_variable条件变量对共享变量进行保护。

这样会导致代码逻辑更加复杂,逻辑细节也会变得非常谨慎。这个时候我们引入future类对以上操作进行简化。

future描述

  • 头文件
  • 声明template< class T > class future;
  • 简介

    future类模版提供了一种异步访问操作机制如下:
    • 异步操作能提供一个 std::future 对象给该异步操作的创建者。其中包括std::asyncstd::packaged_taskstd::promise的返回值为std::future对象
    • 异步操作的创建者能用各种方法查询、等待或从 std::future 提取值。若异步操作仍未提供值,则这些方法可能阻塞。如使用std::async创建的异步操作返回的future对象未获取值,那么std::async将不会退出
    • 异步操作准备好发送结果给创建者时,它能通过修改链接到创建者的 std::future 的共享状态(例如std::promise::set_value)进行

future类成员使用

  • std::future::get 获取异步操作的返回结果

    get 方法等待直至 future 拥有合法结果并(依赖于使用哪个模板)获取它。它等效地调用 wait() 等待结果

    这里需要简单说明下get函数的返回值,它的返回值为std::move()类型,即将之前的返回结果包括值和地址都进行赋值。如果二次尝试get,回访问到空的值和地址,报出异常std::future_error

    我们对刚开始的代码更改如下:

    #include 
    #include 
    #include 
    #include using namespace std;int fun1(int n) { int res = 1;for (int i = n; i>1; --i) { res *=i;}cout << "Result is " << res << endl;return res;
    }int main()
    { int x;//std::thread t1(fun1,4,std::ref(x));//t1.join();//我们使用异步操作,并使用get函数获取异步操作函数的合法返回值std::future<int> fu = std::async(fun1,4);x = fu.get();return 0;
    }
    

    如上代码,我们使用std::async()异步操作来创建字线程,并返回一个future类的对象,然后由future对象的get()函数来获取子线程合法的返回结果。

    关于async()等异步操作将会在下一篇进行分享,这里简单描述一下:

    async提供的是从子线程获取数值到主线程,如果我们想要从主线程获取数值到子线程,则需要promise异步操作

    查看如下代码为我们利用promise从主线程获取数值到子线程

    #include 
    #include 
    #include 
    #include using namespace std;int fun1(std::future<int> &f) { int res = 1;int n = f.get();for (int i = n; i>1; --i) { res *=i;}cout << "Result is " << res << endl;return res;
    }int main()
    { int x;//std::thread t1(fun1,4,std::ref(x));//t1.join();std::promise<int> p;//标示f是一个需要从未来获取数值future对象std::future<int> f = p.get_future(); std::future<int> fu = std::async(std::launch::async,fun1, std::ref(f));//为f设置数值,在子线程中进行f.get()获取主线程到数值p.set_value(4);x = fu.get();cout << "Get from child " << x << endl;return 0;
    }
    

    输出结果如下:

    Result is 24
    Get from child 24
    

    可以看到最终从子线程获取到了计算好的合法数值

  • std::future::wait 阻塞线程,直到获取到可用结果

    #include 
    #include 
    #include int fib(int n)
    { if (n < 3) return 1;else return fib(n-1) + fib(n-2);
    }int main()
    { std::future<int> f1 = std::async(std::launch::async, [](){ return fib(20);});std::future<int> f2 = std::async(std::launch::async, [](){ return fib(25);});std::cout << "waiting...
    ";//主线程在该处阻塞,直到获取到f1,f2两个线程到合法的结果f1.wait();f2.wait();std::cout << "f1: " << f1.get() << '
    ';std::cout << "f2: " << f2.get() << '
    ';
    }
    
  • wait_for()wait_until()两个函数成员同condition_variable中的类似,仍然是仅增加了需要等待的时间,到达等待的时间如仍然无法获取到合法的返回值即同样终止get

总结

可以看到我们在使用future的类的时候能够极为方便得实现线程之间的变量传递,达到更加便捷且更加安全的线程之间数据共享的目的。

关于future类的异步操作async,promise以及packaged_task的使用方式将后续分享。

更多相关:

  • 经过长期探索,发现一个不需要手动设置线程休眠时间(e.g. std::this_thread::sleep_for(std::chrono::microseconds(1)))的代码: Github: https://github.com/log4cplus/ThreadPool #ifndef THREAD_POOL_H_7e...

  • nth_element(first,nth,last) first,last 第一个和最后一个迭代器,也可以直接用数组的位置。  nth,要定位的第nn 个元素,能对它进行随机访问. 将第n_thn_th 元素放到它该放的位置上,左边元素都小于它,右边元素都大于它. 测试代码: http://www.cplusplus.com...

  • c/c++老版本的rand()存在一定的问题,在转换rand随机数的范围,类型或者分布时,常常会引入非随机性。 定义在 中的随机数库通过一组协作类来解决这类问题:随机数引擎 和 随机数分布类 一个给定的随机数发生器一直会生成相同的随机数序列。一个函数如果定义了局部的随机数发生器,应该将(引擎和分布对象)定义为 st...

  • jsoncpp 是一个C++ 语言实现的json库,非常方便得支持C++得各种数据类型到json 以及 json到各种数据类型的转化。 一个json 类型的数据如下: {"code" : 10.01,"files" : "","msg" : "","uploadid" : "UP000000" } 这种数据类型方便我们人阅读以...

  • 问题如下: 已知一组数(其中有重复元素),求这组数可以组成的所有子集中,子 集中的各个元素和为整数target的子集,结果中无重复的子集。 例如: nums[] = [10, 1, 2, 7, 6, 1, 5], target = 8 结果为: [[1, 7], [1, 2, 5], [2, 6], [1, 1, 6]] 同样之前有...

  •         Apache POI是一个开源的利用Java读写Excel,WORD等微软OLE2组件文档的项目。        我的需求是对Excel的数据进行导入或将数据以Excel的形式导出。先上简单的测试代码:package com.xing.studyTest.poi;import java.io.FileInputSt...

  • 要取得[a,b)的随机整数,使用(rand() % (b-a))+ a; 要取得[a,b]的随机整数,使用(rand() % (b-a+1))+ a; 要取得(a,b]的随机整数,使用(rand() % (b-a))+ a + 1; 通用公式:a + rand() % n;其中的a是起始值,n是整数的范围。 要取得a到b之间的...

  • 利用本征图像分解(Intrinsic Image Decomposition)算法,将图像分解为shading(illumination) image 和 reflectance(albedo) image,计算图像的reflectance image。 Reflectance Image 是指在变化的光照条件下能够维持不变的图像部分...

  • 题目:面试题39. 数组中出现次数超过一半的数字 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。 你可以假设数组是非空的,并且给定的数组总是存在多数元素。 示例 1: 输入: [1, 2, 3, 2, 2, 2, 5, 4, 2] 输出: 2 限制: 1 <= 数组长度 <= 50000 解题: cl...

  • 题目:二叉搜索树的后序遍历序列 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。 参考以下这颗二叉搜索树:      5     /    2   6   /  1   3示例 1: 输入: [1,6,3,2,5] 输出...