《探索C++多线程》:future源码(一)

来源:互联网 发布:微信号人肉软件 编辑:程序博客网 时间:2024/05/20 19:16

        在此之前的几篇文章分别介绍和分析了C++11.0标准库中支持多线程的几个头文件:<thread>、<mutex>、<condition_variable>。那么接下来乘热打铁,在这篇文章中将要分析的是:多线程的异步操作。

        多线程的异步操作源码在头文件<future>中。我们先来看一看<future>中都定义了些什么类、函数:

classesfuturefuture_errorpackaged_task promiseshared_futureenum classesfuture_errcfuture_statuslaunch

functionsasyncfuture_categoy


        从上表中可以看出,<future>中提供的外部接口就有6个class,3个enum class,2个function,在这篇文章中,我们先来分析上表中 用颜色标记的几个接口,剩下的将在下一篇博文中进行分析。

std::future

        future提供了用来访问异步操作结果的机制。当一个异步操作我们不可能马上获取操作结果时,就可以用同步等待的方式来获取,通过查询future的状态(future_status)来获取异步操作结果。

        future的状态定义在future_status中,如下:

enum class future_status {ready,// 异步操作已完成,共享状态变为了readytimeout,// 异步操作超时,在规定时间内共享状态没有变为readydeferred// 异步操作还没开始,共享状态包含了一个deferred函数};

class future中提供了几个方法:get()、wait()、wait_for()、wait_until()。在介绍future之前,先来介绍一个函数:std::async。

std::async

返回future对象,其关联的异步状态管理一个函数对象。可能从概念上我们还把握不到什么,下面看一段代码:

#include <iostream>       // std::cout#include <future>         // std::async, std::futureusing namespace std;// 这是一个用于检验素数的函数bool is_prime(int x) {    cout << "Calculating. Please, wait...\n";    for (int i = 2; i < x; ++i) {        if (x % i == 0) {            return false;        }    }    return true;}int main(){    // 异步地调用函数is_prime(313222313)    future<bool> fut = async(is_prime, 313222313);    cout << "Checking whether 313222313 is prime.\n";    bool ret = fut.get();      // 等待is_prime返回    if (ret) {        cout << "It is prime!\n";    } else {        cout << "It is not prime.\n";    }    return 0;}

        我们从代码中看到,async关联了is_prime函数且传入参数为313222313,async返回一个关联了异步状态的future对象。在关联完is_prime函数后,便异步地执行main函数和is_prime函数(实际上就是两个线程了)。而is_prime的返回值,可以通过future对象(代码中的fut)的get()方法获取。这里注意,在main中调用fut.get(),如果is_prime函数已经执行完了,那么可以直接获取到其返回值;如果is_prime函数还没有执行完,那么将阻塞直到得到其返回值。

        实际上,在std::async中的第一个参数可以设置关联函数的异步启动策略,可以设定如下:

future<bool> fut = async(luanch::async, is_prime, 313222313);// 1future<bool> fut = async(luanch::defered, is_prime, 313222313);// 2future<bool> fut = async(luanch::any, is_prime, 313222313);// 3future<bool> fut = async(luanch::sync, is_prime, 313222313);// 4
其中这四种异步启动策略,定义如下 ,另外在后面会我也会讲解到。

// 异步启动的策略enum class launch {async = 0x1,// 异步启动,在调用std::async()时创建一个新的线程以异步调用函数,并返回future对象;deferred = 0x2,// 延迟启动,在调用std::async()时不创建线程,直到调用了future对象的get()或wait()方法时,才创建线程;any = async | deferred,// 自动,函数在某一时刻自动选择策略,这取决于系统和库的实现,通常是优化系统中当前并发的可用性sync = deferred};
讲了这么多,我们现在来总结一下std::async:

        1、异步接口std::async可以自动创建线程去调用线程函数,并返回一个std::future对象,能方便的获取线程的执行结果;

        2、提供了函数的异步启动策略,可以延迟启动。

到此为止,我们再回过头来接着讲std::future,实际上,future对象的获取,可以通过以下三种办法得到:

        1、async
        2、promise::get_future
        3、packaged_task::get_future

其中方法1,我们已经在std::async中接触过了,那么后面的方法2、3,将在讲解std::promise和std::package_task时会讲到。

接下来,我们继续分析std::future对象的几种方法:get()、wait()、wait_for()、wait_until()。

std::future::get()

        1、当共享状态就绪时,返回值存放在共享状态中或抛出异常;

        2、当共享状态尚未就绪,则将阻塞线程并等待,直到准备就绪;

        3、一旦共享状态准备就绪,函数就不阻塞了,将会返回(或抛出)并释放共享状态,使future对象不再有效,即成员函数在每个future对象的共享状态下至多调用一次。

std::future::wait()

        1、等待共享状态就绪。若共享状态尚未准备就绪,则将阻塞线程并等待,直到准备就绪;

        2、一旦共享状态准备就绪,函数就不阻塞了,并返回其值(既不读取值,也不抛出异常)。

我们来看一个例子:

#include <iostream>       // std::cout#include <future>         // std::async, std::future#include <chrono>         // std::chrono::millisecondsusing namespace std;bool is_prime (int x) {  for (int i = 2; i < x; ++i) {  if (x % i == 0) {  return false;  }  }  return true;}int main () {  future<bool> fut = async (is_prime, 194232491);   cout << "checking...\n";  fut.wait();// 等待共享状态就绪  cout << "\n194232491 ";  if (fut.get())      // 由于wait()已保证了共享状态已经就绪,所以get()将不会阻塞    cout << "is prime.\n";  else    cout << "is not prime.\n";  return 0;}
std::future::wait_for()

        1、在一定时间内等待共享状态就绪;

        2、如果共享状态尚未就绪,则将阻塞线程并等待,直到就绪或经过设定的时间rel_time;

举个例子:

#include <iostream>       // std::cout#include <future>         // std::async, std::future#include <chrono>         // std::chrono::millisecondsbool is_prime (int x) {  for (int i = 2; i < x; ++i) {  if (x % i == 0) {  return false;  }  }  return true;}int main () {  future<bool> fut = async (is_prime, 700020007);   cout << "checking, please wait";  chrono::milliseconds span (100);  while (fut.wait_for(span) == future_status::timeout) { // 若超时,则继续wait_forcout << '.';  }  bool x = fut.get(); // 此时能保证共享状态已经就绪,因此get()不会阻塞  cout << "\n700020007 " << (x ? "is" : "is not") << " prime.\n";  return 0;}
std::future::wait_until()

        1、等待共享状态就绪,直到指定的时间点到来;

        2、如果共享状态尚未就绪,则将阻塞线程并等待,直到就绪或指定的时间点到来。


        好了,以上就是std::future的用法了。另外,我们可以注意到,在上面的代码中用到了future_status::timeout来指示函数返回的原因,future_staus是一个enum class,其定义如下:

enum class future_status {ready,// 共享状态就绪timeout,// 超时deferred// 共享状态包含了延迟(std::async使用了参数std::launch::defered)};

1 0