文档章节

Multi thread: std::async()和std::future(1)

SHIHUAMarryMe
 SHIHUAMarryMe
发布于 2016/05/10 21:10
字数 2462
阅读 299
收藏 2
点赞 2
评论 0

对于初学者而言,“以多线程运行程序”的最佳起点就是C++标准库中的 std::async()class std::future提供的高级接口.

(1),std::async()提供一个接口,让一段机能或者说一个callable object function(可调用函数对象)若是可能的话在后台运行成为一个独立的线程.

(2),std::future允许你等待线程结束并获取其结果(一个返回值,也有可能是一个异常).

#include <iostream>
#include <future>
#include <chrono>
#include <random>
#include <exception>

int doSomething(const char& c)
{
	std::default_random_engine engine(c);
	std::uniform_int_distribution<int> id(10, 1000);
	
	for(int i=0; i<10; ++i){
		std::this_thread::sleep_for(std::chrono::milliseconds(id(engine)));
		std::cout.put(c).flush();
	}
	
	return static_cast<int>(c);
}

int func1()
{
	return doSomething(',');
}

int func2()
{
	return doSomething('+');
}

int main()
{
	std::cout<< "start func1() in background"
	         << "and func2() in foreground: "<<std::endl;
	
	//开始执行func1,也许是立即被放到后台,也许是待会放到后台,也许永远不会放到后台.     
	std::future<int> result1(std::async(func1, std::launch::async | std::launch::deferred)); //通过std::async(func1)启动func1并尝试把他放到后台. 
	
	int result2 = func2();
	int result = result1.get() + result2;
	
	std::cout<< "\nresult of func1()+func2(): "<<result<<std::endl;
	
	return 0;
}

上面的代码中我们使用了:

std::future<int> result1(std::async(func1));这里,理论上std::async()尝试将其所获得的函数立刻异步启动于一个独立线程内。因此概念上func1()在这里就被启动了,不会造成main线程被阻塞(block).

但是需要注意的是:

1,虽然std::future能让你取得传给std::async()的那个callable object”的返回值(如果那个callable object执行正常那么获得的是返回值,否则是个异常)。

2,std::future确保callable object会被调用,注意先前说的std::async()尝试启动目标函数.如果callable object并没有被立即启动,稍后我们需要这个future object才能强迫执行它(但我们需要函数的运行结果或当我们想要确保该函数被执行时).

 

结合上面的代码(特别需要注意的是: std::launch::async|std::launch::deferred):

int result = result1.get() + result2; 随着get()调用,以下三件事之一可能会发生:

1,如果func1()被async()启动且已经执行结束,会立刻获得其结果.

2,如果func1()被启动但是尚未结束,get()会引发main线程阻塞(block)直至func1()结束后获得结果.

3,如果func1()尚未启动,会强迫启动func1()在一个独立的线程且引起main线程(block)直到func1()执行完毕.

std::future<int> result1(std::async(func1));

result1.get();

这样的组合带来的好处就是:

1,如果可能,当main线程处理到std::future<int> result1(std::async(func1))时func1()可能被并行运行.

2,如果无法并行运行,那么会在执行到 result1.get()时被串行(serial)调用,这就意味着无论如何都能保证func1()被调用!.

 

(2), std::async()

template< class Function, class... Args>
[[nodiscard]] std::future<std::invoke_result_t<std::decay_t<Function>, std::decay_t<Args>...>>
    async( Function&& f, Args&&... args );



template< class Function, class... Args >
[[nodiscard]] std::future<std::invoke_result_t<std::decay_t<Function>, std::decay_t<Args>...>>
    async( std::launch policy, Function&& f, Args&&... args );

 

其实就是一个template function,返回一个std::future.该函数可接受三种类型的参数:

1) std::launch: 

   1,std::launch::async 它会告诉std::async()立即以并行的方式启动callable object。如果异步调用在此无法实现,程序会抛出一个std::system_error的异常.

 2,std::launch::deferred 它告诉std::async()对目标函数进行延缓执行,这保证目标函数绝对不会在没有get()和wait()调用的被启用,这也就意味着不再是单独的使用一个线程了,也同样是在main线程中执行.

3,std::lanuch::async | std::lanuch::deferred 它告诉std::async()启用目标函数的时候自动选择policy.

 2) callable object

 1,可以是函数

 2,可以是重载了operator()的struct/class.

3)Arags&&...

1,一个或多个参数(可以不同类型)作为callable object的参数(parameters).

4),[nodiscard]

这个是c++11以来的新特性,前缀这个意味着我们不能忽略该函数的返回值.

具体参阅这里:http://en.cppreference.com/w/cpp/language/attributes

demo1: lazy求值:

比如下面的例子,不用每次都求出task1和task2的返回值.只需要求出一个我们需要返回值就可以了.

auto f1 = std::async(std::launch::deferred, task1);
auto f2 = std::async(std::launch::deferred, task2);
.........
auto val = jundge() ? f1.get() : f2.get();

 

(3) std::future

我们接着来看一下std::future:

1)

template <class T>  future;
template <class R&> future<R&>;     // 特例化 : T is a reference type (R&)
template <>         future<void>;   // 特例化 : T is void

从上面我们可以看出来class std::future<>针对引用类型和void类型进行了特例化.

2)constructor

future() noexcept;
future (const future&) = delete;	
future (future&& x) noexcept;

class std::future<>的拷贝构造函数是被删除了的,只能被移动构造.

 

3) member function(s)

std::future::get();

T get();
R& future<R&>::get();       // 特例化版本: when T is a reference type (R&)
void future<void>::get();   // 特例化版本: when T is void

一个std::future只能调用一次get();在调用了get()之后当前class std::future<>就会处于无效状态,该class std::future的状态只能通过valid()来检测.

1,如果callable object被async()启动(无论是并行还是串行调用)且已经执行结束,会立刻获得其结果.

2,如果callable object被启动但是尚未结束:

  如果,我们指定的策略为std::launch::async,这个时候callable object在另外一个线程中执行,因此会阻塞main线程直到,该callable object所在的线程执行完成.

  如果,我们指定的策略为std::launch::deferred,那么此时callable object会在当前main线程中执行(其实就是串行辣).

 如果,指定的策略为std::launch::async | std::launch::deferred,那么取决于编译器,执行情况会是上面的两种.

 

std::future::valid()

bool valid() const noexcept;

如果当前std::future object没有调用过std::future::get()返回true,否则返回false.

 

std::future::wait();

void wait() const;

在std::future中有个shared state默认情况下是not ready:

如果当前std::future对象调用wait()的时候shared state处于未not ready状态,那么wait()就会阻塞(block)正在调用wait()的线程直到callable object执行完成,且把sheared state设置为ready这样就意味着我们在下面的代码中直接调用get()就会直接获得callable object的返回值.

demo 2 for wait:

#include <iostream>
#include <chrono>
#include <future>


bool is_prime(const int& number)
{
	for(int i=2; i<number; ++i){
		if(number%i == 0){
			return false;
			
		}
	}
	
	return true;
}

int main()
{
	std::future<bool> result(std::async(is_prime, 194232491));
	
	std::cout<<"check...."<<std::endl;
	result.wait(); //线程在这里停顿了一下. 
	
	std::cout<<"194232491\n";
	if(result.get()){ //这里则是直接出来了结果. 
		std::cout<<"is prime"<<std::endl;
		
	}else{
		std::cout<<"is not prime"<<std::endl;
	}
	
	return 0;
}

std::future::wait_until() 和 std::future::wait_for()

在了解这两个函数之前我们需要了解一下std::future的三种状态:

std::future_status::ready  ------------ std::future中的shared state已经被设置为ready,且callable object已经被产生了一个值或者异常.

std::future_satus::timeout  ------- --- std::future中的shared state在指定的时间段(std::chrono::duration)或者到了指定的时间点(std::chrono::time_point)还没有被设置为ready.(异步操作超时)

std::future_status::defferred -------------如果std::async()延缓了callable object的调用而后续的程序中又完全没调用wait()或者get(),那么wait_until()和wait_for()就会返回这个.

#include <iostream>
#include <chrono>
#include <future>

bool is_prime(const int& number)
{
	for(int i=2; i<number; ++i){
		if(number%i == 0){
			return false;
		}
	}
	
	return true;
}

int main()
{
	std::future<bool> result(std::async(is_prime, 700020007));
	
	std::cout<<"checking please waiting"<<std::endl;
	std::chrono::milliseconds span(100);
	
	while(result.wait_for(span) == std::future_status::timeout){ //检查是否超时. 
    //但是请注意上这里的循环可能永远不会结束,因为可能在单线程的环境中,将被推迟到get()被调用的时候,才调用callable object.
    //因此如果调用std::async()并且给他指定了launch类型为std::launch::async;或者没有指定launch类型
    //最好使用result.wait_for(span) == std::future_status::ready或者 std::future_status::deferred
		std::cout<<".";
    }
    
	bool x = result.get();
	std::cout << "\n700020007 " << (x?"is":"is not") << " prime.\n";
	
	return 0;
}

std::future<>::wait_for()

template <class Rep, class Period>
  future_status wait_for (const chrono::duration<Rep,Period>& rel_time) const;

阻塞调用wait_for()的线程rel_time时间段等待std::future的shared state被设置被设置为ready(如果此时callable object已经在后台调用完成那么返回std::future_status::ready)否则返回std::future_status::timeout,但是如果在std::async()调用了callable object的时候使用了std::launch::deferred(推迟调用callable object),wait_for()是不会强制启动对callable object的调用的,也会返回一个std::future_status的状态.

// future::wait_for
#include <iostream>       // std::cout
#include <future>         // std::async, std::future
#include <chrono>         // std::chrono::milliseconds

// a non-optimized way of checking for prime numbers:
bool is_prime(int x) {
	for (int i = 2; i<x; ++i) if (x%i == 0) return false;
	return true;
}

int main()
{
	// call function asynchronously:
	std::future<bool> fut = std::async(is_prime, 700020007);

	std::cout << "checking, please wait";

	std::chrono::milliseconds span(10);
	while(fut.wait_for(span) == std::future_status::timeout)
		std::cout << '.';

	std::cout << "-------";
	bool x = fut.get();

	std::cout << "\n700020007 " << (x ? "is" : "is not") << " prime.\n";

	return 0;
}

std::future::wait_until

template <class Clock, class Duration>
  future_status wait_until (const chrono::time_point<Clock,Duration>& abs_time) const;

阻塞调用wait_until()的线程直到abs_time这个时间点或者callable object已在后台调用完成(无论是前面的哪种情况先发生都会返回一个std::future_status):但是如果在std::async()调用了callable object的时候使用了std::launch::deferred(推迟调用callable object),wait_until()是不会强制启动对callable object的调用的,也会返回一个std::future_status的状态

 

std::future::shared

shared_future<T> share();

返回一个std::shared_future,其实是返回当前std::future的shared state,返回之后当前std::future不再可用且当前std::future的shared state变成invalid.

#include <iostream>
#include <future>
#include <chrono>
#include <thread>

int getValue()
{
	std::this_thread::sleep_for(std::chrono::seconds(5));
	return 10;
}

int main()
{
	std::future<int> result = std::async(std::launch::async, getValue); //并行运行了getValue.
	std::shared_future<int> shared_result = result.share();     //此时的getValue还在后台运行.

	std::cout << std::boolalpha << result.valid() << std::endl;
	std::cout << shared_result.get() << std::endl;
	std::cout << shared_result.get() << std::endl;

	return 0;
}

 

© 著作权归作者所有

共有 人打赏支持
SHIHUAMarryMe
粉丝 12
博文 165
码字总数 138772
作品 0
武汉
程序员
Multi thread: std::shared_future(2)

在之前我们了解了class std::future,但是通过class std::future获得的结果只能get()一次,第二次调用就会产生不可预期的结果,通常会抛出一个std::future_error。 但是当多个其他线程想处理另...

SHIHUAMarryMe ⋅ 2016/05/13 ⋅ 0

Multi thread: std::promise

前面我们了解到可以使用std::sharedfuture/std::sharedfuture在多个其他线程中处理另外一个线程的函数的返回结果. 那么疑问来了,我们要怎样在多线程之间传递数据呢? demo1 :std::promise在线...

SHIHUAMarryMe ⋅ 2016/05/14 ⋅ 0

C++11实现跨平台线程池

生产者消费者 转自:http://www.cnblogs.com/sanjin/archive/2012/08/09/2629890.html std::conditionvariable的使用方法如下: ·当持有锁(mutex)之后,线程调用wait ·wait解开持有的互斥锁...

初雪之音 ⋅ 2016/03/19 ⋅ 0

C++11: std::thread和std::this_thread

C++11起,标准库除了提供std::async(),这样的高级线程接口以外,还提供了更加简单的std::thread,我们只需要声明一个std::thread对象,然后传递进去函数(function注意这里并不是callable obje...

SHIHUAMarryMe ⋅ 2016/03/07 ⋅ 0

C++11: std::packaged_task

class std::packaged_task被用来同时存储callable object,以及该callable object的返回值(也可能是个异常). ----------------------------我是分割线------------------------------ std:......

SHIHUAMarryMe ⋅ 2016/05/15 ⋅ 0

boost asio 多线程异步服务器历程

场景说明 本例子支持多线程异步处理消息,针对每一个链接请求,创建线程处理稍后的指令,CSimpleSession::SessionThreadFunc是线程函数,asyncreadsome函数设置接收数据的回调函数ContinueRea...

技术小胖子 ⋅ 2017/11/08 ⋅ 0

boost async_read_some 用法

asyncreadsome读到数据就会直接回调设置的函数,不管数据是否已经读完。所以在这里 会遇到一个非常棘手的问题,如何确定数据已经读取完毕?常见的方式是在数据的后面添加 标志位,例如添加/...

技术小胖子 ⋅ 2017/11/09 ⋅ 0

boost中asio的deadline_timer异步调用示例

include <iostream> include <boost/asio.hpp> include <boost/bind.hpp> include <boost/datetime/posixtime/posix_time.hpp> void print(const boost::system::errorcode&, boost::asio::d......

谢中辉 ⋅ 2011/09/17 ⋅ 0

c++ 11 多线程处理(1)

几种线程的方式 创建线程 通过函数指针来创建一个线程 使用函数对象创建一个线程 使用 匿名函数来创建线程 不同线程之间的区别 第一个线程都会有一个ID成员函数指定关联线程对象的IDstd::th...

罗布V ⋅ 2016/07/07 ⋅ 0

C++11 并发编程教程 - Part 3 : 锁的进阶与条件变量

上一篇文章中我们学习了如何使用互斥量来解决一些线程同步问题。这一讲我们将进一步讨论互斥量的话题,并向大家介绍 C++11 并发库中的另一种同步机制 —— 条件变量。 递归锁 考虑下面这个简...

天下杰论 ⋅ 2013/12/29 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

高并发之Nginx的限流

首先Nginx的版本号有要求,最低为1.11.5 如果低于这个版本,在Nginx的配置中 upstream web_app { server 到达Ip1:端口 max_conns=10; server 到达Ip2:端口 max_conns=10; } server { listen ...

算法之名 ⋅ 今天 ⋅ 0

Spring | IOC AOP 注解 简单使用

写在前面的话 很久没更新笔记了,有人会抱怨:小冯啊,你是不是在偷懒啊,没有学习了。老哥,真的冤枉:我觉得我自己很菜,还在努力学习呢,正在学习Vue.js做管理系统呢。即便这样,我还是不...

Wenyi_Feng ⋅ 今天 ⋅ 0

博客迁移到 https://www.jianshu.com/u/aa501451a235

博客迁移到 https://www.jianshu.com/u/aa501451a235 本博客不再更新

为为02 ⋅ 今天 ⋅ 0

win10怎么彻底关闭自动更新

win10自带的更新每天都很多,每一次下载都要占用大量网络,而且安装要等得时间也蛮久的。 工具/原料 Win10 方法/步骤 单击左下角开始菜单点击设置图标进入设置界面 在设置窗口中输入“服务”...

阿K1225 ⋅ 今天 ⋅ 0

Elasticsearch 6.3.0 SQL功能使用案例分享

The best elasticsearch highlevel java rest api-----bboss Elasticsearch 6.3.0 官方新推出的SQL检索插件非常不错,本文一个实际案例来介绍其使用方法。 1.代码中的sql检索 @Testpu...

bboss ⋅ 今天 ⋅ 0

informix数据库在linux中的安装以及用java/c/c++访问

一、安装前准备 安装JDK(略) 到IBM官网上下载informix软件:iif.12.10.FC9DE.linux-x86_64.tar放在某个大家都可以访问的目录比如:/mypkg,并解压到该目录下。 我也放到了百度云和天翼云上...

wangxuwei ⋅ 今天 ⋅ 0

PHP语言系统ZBLOG或许无法重现月光博客的闪耀历史[图]

最近在写博客,希望通过自己努力打造一个优秀的教育类主题博客,名动江湖,但是问题来了,现在写博客还有前途吗?面对强大的自媒体站点围剿,还有信心和可能型吗? 至于程序部分,我选择了P...

原创小博客 ⋅ 今天 ⋅ 0

IntelliJ IDEA 2018.1新特性

工欲善其事必先利其器,如果有一款IDE可以让你更高效地专注于开发以及源码阅读,为什么不试一试? 本文转载自:netty技术内幕 3月27日,jetbrains正式发布期待已久的IntelliJ IDEA 2018.1,再...

Romane ⋅ 今天 ⋅ 0

浅谈设计模式之工厂模式

工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 在工厂模式中,我们在创建对象时不会对客户端暴露创建逻...

佛系程序猿灬 ⋅ 今天 ⋅ 0

Dockerfile基础命令总结

FROM 指定使用的基础base image FROM scratch # 制作base image ,不使用任何基础imageFROM centos # 使用base imageFROM ubuntu:14.04 尽量使用官方的base image,为了安全 LABEL 描述作...

ExtreU ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部