对于初学者而言,“以多线程运行程序”的最佳起点就是C++标准库中的 std::async() 和 class std::future提供的高级接口.ios
(1),std::async()提供一个接口,让一段机能或者说一个callable object function(可调用函数对象)如果可能的话在后台运行成为一个独立的线程.c++
(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; }
上面的代码中咱们使用了:dom
std::future<int> result1(std::async(func1));这里,理论上std::async()尝试将其所得到的函数马上异步启动于一个独立线程内。所以概念上func1()在这里就被启动了,不会形成main线程被阻塞(block).异步
可是须要注意的是:async
1,虽然std::future能让你取得“传给std::async()的那个callable object”的返回值(若是那个callable object执行正常那么得到的是返回值,不然是个异常)。函数
2,std::future确保callable object会被调用,注意先前说的std::async()尝试启动目标函数.若是callable object并无被当即启动,稍后咱们须要这个future object才能强迫执行它(但咱们须要函数的运行结果或当咱们想要确保该函数被执行时).this
结合上面的代码(特别须要注意的是: std::launch::async|std::launch::deferred):spa
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; }