c++11异步编程

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/D_Guco/article/details/70429755

     在c++11之前我们想要获取一个线程的执行结果,通常的的手段就是给线程传一个指针或者引用,然后把执行结果给这个指针或者引用,这样我们就可以在其他的线程例获取执行结果了,但是这种方法有一个缺点就是,我们并不知道什么时候可以得到这个执行结果,换句话说就是不知道这个结果什么时候是正确的,我们不得不每时每刻的去检查这个线程的执行结果。不过c++11中添加了对异步编程的支持,我们可以通过std::future获取我们关心的执行结果,虽然没有java中的异步操作丰富强大,但是对c++开发者来说已经方便了很多了。
     与之相关的有std::promise, std::package_task,std::future, std::shared_future,std::async()等
     Promise 对象可以保存某一类型的值,该值可被 future 对象读取(可能在另外一个线程中),因此 promise 也提供了一种线程同步的手段。在 promise 对象构造时可以和一个共享状态(通常是std::future)相关联,并可以在相关联的共享状(std::future)上保存一个类型为 T 的值。可以通过 get_future 来获取promise 对象相关联的 future 对象,调用该函数之后,两个对象共享相同的共享状态(shared state)。promise 对象是异步 Provider(提供者,可以理解为工厂),它可以在某一时刻设置共享状态的值。future 对象可以异步返回共享状态的值,或者在必要的情况下阻塞调用者并等待共享状态标志变为 ready,然后才能获取共享状态的值。下面看一个简单的例子

#include        // std::cin, std::cout, std::ios
#include      // std::ref
#include          // std::thread
#include          // std::promise, std::future
#include       // std::exception, std::current_exception

void task(std::promise& prom)
{
    int x = 0;
    /*
     * 这里一般一个非常耗时耗cpu的操作如查找,计算等,在此过程中得到x的最终值,这里我们直接赋值为10
     */
    x = 10;
    prom.set_value(10);         //设置共享状态的值,同时promise会设置为ready
}

void print_int(std::future& fut) {
    int x = fut.get();          //如果共享状态没有设置ready,调用get会阻塞当前线程
    std::cout << "value: " << x << '\n';
}

int main ()
{
    std::promise prom;                           // 生成一个 std::promise 对象.
    std::future fut = prom.get_future();         // 和 future 关联.
    std::thread th1(task, std::ref(prom));            // 将 prom交给另外一个线程t1 注:std::ref,promise对象禁止拷贝构造,以引用方式传递
    std::thread th2(print_int, std::ref(fut));        // 将 future 交给另外一个线程t.
    /*
     *主线程这里我们可以继续做一大堆我们想做的事,不用等待耗时的task线程,也不会因为等待task的执行结果而阻塞
     */
    
    th1.join();
    th2.join();
    return 0;
}

      std::packaged_task 包装一个可调用的对象,并且允许异步获取该可调用对象产生的结果,从包装可调用对象意义上来讲,std::packaged_task将其包装的可调用对象的执行结果传递给一个 std::future 对象(该对象通常在另外一个线程中获取 std::packaged_task 任务的执行结果)。std::packaged_task 对象内部包含了两个最基本元素,一、被包装的任务(stored task),任务(task)是一个可调用的对象,如函数指针、成员函数指针或者函数对象,二、共享状态(shared state),用于保存任务的返回值,可以通过 std::future 对象来达到异步访问共享状态的效果。可以通过 std::packged_task::get_future 来获取与共享状态相关联的 std::future 对象。在调用该函数之后,两个对象共享相同的共享状态,具体解释如下:std::packaged_task 对象是异步Provider,它在某一时刻通过调用被包装的任务来设置共享状态的值。std::future 对象是一个异步返回对象,通过它可以获得共享状态的值,当然在必要的时候需要等待共享状态标志变为 ready.
    
#include 
#include 
#include 
#include 

int countdown (int from, int to) {
    for (int i=from; i!=to; --i) {
        std::cout << i << '\n';
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
    std::cout << "Finished!\n";
    return from - to;
}

int main ()
{
    std::packaged_task task(countdown); // 设置 packaged_task
    std::future ret = task.get_future(); // 获得与 packaged_task 共享状态相关联的 future 对象.
    
    std::thread th(std::move(task), 10, 0);   //创建一个新线程完成计数任务.
    
    int value = ret.get();                    // 等待任务完成并获取结果.
    
    std::cout << "The countdown lasted for " << value << " seconds.\n";
    
    th.join();
    return 0;
}

     这里我们可以看到两者的区别,创建线程时,上面传的是一个函数,这里却是一个package_task对象,它实际上做了简单的封装,简化了future的使用方法。当然还有更简单的写法就是用std::async来创建future:future<T> myFuture=async(task,10),在task中操作后,通过myFuture获取执行结果,我们甚至不用手动的创建线程,编译器会帮我们自动创建一个线程去执行。
    shared_future和future的区别是:一个future对象和一个共享状态相关,且转移只能通过move语义。但是多个shared_future对象可以和共享状态相关(即多对一)。std::shared_future 与 std::future 类似,但是 std::shared_future 可以拷贝、多个 std::shared_future 可以共享某个共享状态的最终结果(即共享状态的某个值或者异常)。shared_future 可以通过某个 std::future 对象隐式转换(参见 std::shared_future 的构造函数),或者通过 std::future::share() 显示转换,无论哪种转换,被转换的那个 std::future 对象都会变为 not-valid.
      
   以上只是一些最基本的用法,总的看来还没有boost库的异步库,java异步接口等功能强大,更全面更详细的介绍看下面的参考资料,我也是个菜鸟。

    参考资料:

     http://blog.csdn.net/liuxuejiang158blog/article/details/17354115

     http://www.cplusplus.com/reference/future/

     https://github.com/forhappy/Cplusplus-Concurrency-In-Practice/

     http://www.2cto.com/kf/201609/548243.html




    

阅读更多
换一批

没有更多推荐了,返回首页