std::future和std::promise

std::future

std::future期待一个返回,从一个异步调用的角度来说,future更像是执行函数的返回值,C++标准库使用std::future为一次性事件建模,如果一个事件需要等待特定的一次性事件,那么这线程可以获取一个future对象来代表这个事件。

异步调用往往不知道何时返回,但是如果异步调用的过程需要同步,或者说后一个异步调用需要使用前一个异步调用的结果。这个时候就要用到future。

线程可以周期性的在这个future上等待一小段时间,检查future是否已经ready,如果没有,该线程可以先去做另一个任务,一旦future就绪,该future就无法复位(无法再次使用这个future等待这个事件),所以future代表的是一次性事件

future的类型

<future>库的头文件中声明了两种future,唯一future(std::future)和共享future(std::shared_future)这两个是参照std::unique_ptr和std::shared_ptr设立的,前者的实例是仅有的一个指向其关联事件的实例,而后者可以有多个实例指向同一个关联事件,当事件就绪时,所有指向同一事件的std::shared_future实例会变成就绪。

future的使用

std::future是一个模板,例如std::future<int>,模板参数就是期待返回的类型,虽然future被用于线程间通信,但其本身却并不提供同步访问,热门必须通过互斥元或其他同步机制来保护访问。

future使用的时机是当你不需要立刻得到一个结果的时候,你可以开启一个线程帮你去做一项任务,并期待这个任务的返回,但是std::thread并没有提供这样的机制,这就需要用到std::async和std::future(都在<future>头文件中声明)

std::async返回一个std::future对象,而不是给你一个确定的值(所以当你不需要立刻使用此值的时候才需要用到这个机制)。当你需要使用这个值的时候,对future使用get(),线程就会阻塞直到future就绪,然后返回该值。

#include <future>
#include <iostream> int find_result_to_add()
{
return 1 + 1;
} void do_other_things()
{
std::cout << "Hello World" << std::endl;
} int main()
{
std::future<int> result = std::async(find_result_to_add);
do_other_things();
std::cout << result.get() << std::endl;
return 0;
}

跟thread类似,async允许你通过将额外的参数添加到调用中,来将附加参数传递给函数。如果传入的函数指针是某个类的成员函数,则还需要将类对象指针传入(直接传入,传入指针,或者是std::ref封装)。

默认情况下,std::async是否启动一个新线程,或者在等待future时,任务是否同步运行都取决于你给的参数。这个参数为std::launch类型

  • std::launch::defered表明该函数会被延迟调用,直到在future上调用get()或者wait()为止
  • std::launch::async,表明函数会在自己创建的线程上运行
  • std::launch::any = std::launch::defered | std::launch::async
  • std::launch::sync = std::launch::defered
enum class launch
{
async,deferred,sync=deferred,any=async|deferred
};

PS:默认选项参数被设置为std::launch::any。如果函数被延迟运行可能永远都不会运行。

std::packaged_task

如果说std::async和std::feature还是分开看的关系的话,那么std::packaged_task就是将任务和feature绑定在一起的模板,是一种封装对任务的封装。

The class template std::packaged_task wraps any Callable target (function, lambda expression, bind expression, or another function object) so that it can be invoked asynchronously. Its return value or exception thrown is stored in a shared state which can be accessed through std::future objects.

可以通过std::packaged_task对象获取任务相关联的feature,调用get_future()方法可以获得std::packaged_task对象绑定的函数的返回值类型的future。std::packaged_task的模板参数是函数签名

PS:例如int add(int a, intb)的函数签名就是int(int, int)

#include <future>
#include <iostream> int add(int a, int b)
{
return a + b;
} void do_other_things()
{
std::cout << "Hello World" << std::endl;
} int main()
{
std::packaged_task<int(int, int)> task(add);
do_other_things();
std::future<int> result = task.get_future();
task(1, 1); //必须要让任务执行,否则在get()获取future的值时会一直阻塞
std::cout << result.get() << std::endl;
return 0;
}

std::promise

从字面意思上理解promise代表一个承诺。promise比std::packaged_task抽象层次低。

std::promise<T>提供了一种设置值的方式,它可以在这之后通过相关联的std::future<T>对象进行读取。换种说法,之前已经说过std::future可以读取一个异步函数的返回值了,那么这个std::promise就提供一种方式手动让future就绪。

#include <future>
#include <string>
#include <thread>
#include <iostream> void print(std::promise<std::string>& p)
{
p.set_value("There is the result whitch you want.");
} void do_some_other_things()
{
std::cout << "Hello World" << std::endl;
} int main()
{
std::promise<std::string> promise; std::future<std::string> result = promise.get_future();
std::thread t(print, std::ref(promise));
do_some_other_things();
std::cout << result.get() << std::endl;
t.join();
return 0;
}

由此可以看出在promise创建好的时候future也已经创建好了

线程在创建promise的同时会获得一个future,然后将promise传递给设置他的线程,当前线程则持有future,以便随时检查是否可以取值。

总结

future的表现为期望,当前线程持有future时,期望从future获取到想要的结果和返回,可以把future当做异步函数的返回值。而promise是一个承诺,当线程创建了promise对象后,这个promise对象向线程承诺他必定会被人设置一个值,和promise相关联的future就是获取其返回的手段。

【C++并发实战】(三) std::future和std::promise的更多相关文章

  1. C++11之std::future和std::promise

    为什么C++11引入std::future和std::promise?C++11创建了线程以后,我们不能直接从thread.join()得到结果,必须定义一个变量,在线程执行时,对这个变量赋值,然后执 ...

  2. C++11之std::future和std::promise和std::std::packaged_task

    为什么C++11引入std::future和std::promise?C++11创建了线程以后,我们不能直接从thread.join()得到结果,必须定义一个变量,在线程执行时,对这个变量赋值,然后执 ...

  3. Java并发编程原理与实战三十一:Future&FutureTask 浅析

    一.Futrue模式有什么用?------>正所谓技术来源与生活,这里举个栗子.在家里,我们都有煮菜的经验.(如果没有的话,你们还怎样来泡女朋友呢?你懂得).现在女票要你煮四菜一汤,这汤是鸡汤, ...

  4. C++11 并发指南四(<future> 详解三 std::future & std::shared_future)

    上一讲<C++11 并发指南四(<future> 详解二 std::packaged_task 介绍)>主要介绍了 <future> 头文件中的 std::pack ...

  5. C++11 并发指南四(<future> 详解三 std::future & std::shared_future)(转)

    上一讲<C++11 并发指南四(<future> 详解二 std::packaged_task 介绍)>主要介绍了 <future> 头文件中的 std::pack ...

  6. 【C/C++开发】C++11 并发指南三(std::mutex 详解)

    本系列文章主要介绍 C++11 并发编程,计划分为 9 章介绍 C++11 的并发和多线程编程,分别如下: C++11 并发指南一(C++11 多线程初探)(本章计划 1-2 篇,已完成 1 篇) C ...

  7. C++并发编程之std::async(), std::future, std::promise, std::packaged_task

    c++11中增加了线程,使得我们可以非常方便的创建线程,它的基本用法是这样的: void f(int n); std::thread t(f, n + 1); t.join(); 但是线程毕竟是属于比 ...

  8. C++ 11新特性:std::future & std::shared_future) (转载)

    上一讲<C++11 并发指南四(<future> 详解二 std::packaged_task 介绍)>主要介绍了 <future> 头文件中的 std::pack ...

  9. C++11 使用异步编程std::async和std::future

    先说明一点:std::asyanc是std::future的高级封装, 一般我们不会直接使用std::futrue,而是使用对std::future的高级封装std::async. 下面分别说一下. ...

随机推荐

  1. 如何查看mysql 默认端口号和修改端口号

    http://blog.itpub.net/26148431/viewspace-1466379/ 1,登录mysql 2,使用命令show global variables like 'port'; ...

  2. [Objective-C语言教程]继承(25)

    面向对象编程中最重要的概念之一是继承.继承允许根据一个类定义另一个类,这样可以更容易地创建和维护一个应用程序. 这也提供了重用代码功能和快速实现时间的机会. 在创建类时,程序员可以指定新类应该继承现有 ...

  3. asp.net c# 虾米音乐API

    最近用到虾米音乐的功能,主要是做一个分享音乐功能,找到好多代码,但是比较杂,有用的很少,因 此在此记录下,方便以后自己使用. 对于第三方网站,只要获取了唯一标识,基本上能抓取一些信息. 虾米 音乐的I ...

  4. Android服务重启

    现在有这样的需求,防止自己的app被其他的应用程序(比如qq手机管家)杀死,该怎么实现呢.我们知道app都是运行在进程中的,android是怎样管理这些进程的呢.要想app不被杀死,只要做到进程不被结 ...

  5. 【3】JMicro微服务-服务超时,重试,重试间隔

    如非授权,禁止用于商业用途,转载请注明出处作者:mynewworldyyl 接下来的内容都基于[2]JMicro微服务-Hello World做Demo 微服务中,超时和重试是一个最基本问题下面Dem ...

  6. ES6之新增const命令使用方法

    hi,我又回来了,今天学习一下const命令. 声明一个常量 const声明一个只读常量,一旦声明,常量的值便不可改变. 例子如下: const food = 12; food = 23; // Un ...

  7. 前端知识总结--BFC

    Block Formatting Context,中文直译为块级格式上下文. 1. BFC的定义 是 W3C CSS 2.1 规范中的一个概念,它决定了元素如何对其内容进行定位,以及与其他元素的关系和 ...

  8. Android Fragment向另一个Activity传值

    1.Fragment内: Intent intent=new Intent(getActivity(),ShowDataActivity.class); //参数1:Fragment所依存的Activ ...

  9. Github概念理解备忘录

    总结: add就是用来建立跟踪,添加文件到缓存区: commit就是把文件缓存区的文件正式加到本地库中: push就是把本地库更新到远程库中: git命令的操作要在仓库所在目录下进行才有效: 在Git ...

  10. 最近用.NET实现DHT爬虫,全.NET实现

    最近用.NET实现DHT爬虫,全.NET实现,大家可以加我QQ交流下  309159808