C++11的异步操作(std::future,std::promise,std::packaged_task,std::async )

        C+11提供了异步操作相关的类,主要有std::future,std::promise,std::packaged_task。

std::future作为异步结果的传输通道,可以很方便地获取线程函数的返回值;std::promise用来包装一个值,将数据和future绑定起来,方便线程赋值;std::packaged_task用来包装一个可调用对象,将函数和future绑定起来,以便异步调用。

std::future

        C++11中增加的线程,使得我们可以非常方便地创建和使用线程,但有时候会有些不便,比如希望获取线程函数的返回结果,就不能直接通过thread.join()得到结果,这时就必须定义一个变量,在线程函数中去给这个变量赋值,然后执行join(),最后得到结果,这个过程是比较繁琐的。thread库提供了future用来访问异步操作的结果,因为一个异步操作的结果不能马上获取,只能在未来某个时候从某个地方获取,这个异步操作的结果是一个未来的期待值,所以被称为future,future提供了获取异步操作结果的通道。我们可以以同步等待的方式来获取结果,可以通过查询future的状态来获取异步操作的结果。future_status有如下3种状态:

        Deferred:异步操作还没有开始。

        Ready:异步操作已经完成。

        TimeOut:异步操作超时。

        我们可以查询future的状态,通过它内部的状态可以知道异步任务的执行情况。

#include <iostream>
#include <future>
#include <iostream>
using namespace std;

int mythread(int val) // 线程入口函数
{
    cout << "mythread run..." << endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(5000)); // 休息5秒
    return val;
}

int main()
{
    ///异步创建一个task
    std::future<int> result = std::async(std::launch::async, mythread, 180);

    std::future_status status;

    do
    {
        status = result.wait_for(std::chrono::seconds(1));
        if (status == std::future_status::deferred)
        {
            cout << "deferred..." << endl;
        }
        else if (status == std::future_status::timeout)
        {
            cout << "timeout..." << endl;
        }
        else if (status == std::future_status::ready)
        {
            cout << "ready..." << endl;
        }

    }while(status != std::future_status::ready);

    cout << "result is " << result.get() << endl;

    return 0;
}

        获取future结果有3种方式:get,wait,wait_for,其中get等待异步操作结束并返回结果,wait只是等待异步操作完成,没有返回值,wait_for是超时等待返回结果。

        上面例子程序的返回如下所示:

mythread run...
timeout...
timeout...
timeout...
timeout...
ready...
result is 180

std::promise

        std::promise将数据和future绑定起来,为获取线程函数中的某个值提供便利,在线程函数中为外面传进来的promise赋值,在线程函数执行完成之后就可以通过promis的future获取该值了。值得注意的是,取值是间接得通过promise内部提供的future来获取的。std::promise的基本用法如下:

#include <iostream>
#include <future>
#include <thread>
#include <utility>
#include <unistd.h>
using namespace std;

void func(int a, std::promise<int>& p)
{
    a += 10;

    p.set_value_at_thread_exit(a);
}

int main()
{
    int a = 20;
    std::promise<int> pr;

    thread t(func, a, std::ref(pr));

    t.join();

    std::future<int> f = pr.get_future();

    auto r = f.get();

    cout << r << endl;

    return 0;
}

std::packaged_task

        std::packaged_task包装了一个可调用对象的包装类(如function,lambda expression,bind expression和another function object),将函数和future绑定起来,以便异步调用,它和std::promise在某种程度上有点像,promise保存了一个共享状态,而packaged_task保存的是一个函数。std::packaged_task的基本用法如下:

#include <iostream>
#include <future>
#include <thread>
#include <utility>
#include <unistd.h>
using namespace std;

int func()
{
    return 8;
}

int main()
{
    std::packaged_task<int()> task(func);

    std::thread t1(std::ref(task));

    t1.join();

    std::future<int> f1 = task.get_future();

    cout << f1.get() << endl;

    return 0;
}

三者的关系

        std::future提供了一个访问异步操作结果的机制,它和线程是一个级别的,属于低层次的对象。在std::future之上的高一层是std::packaged_task和std::promise,它们内部都有future以便访问异步操作结果,std::packaged_task包装的是一个异步操作,而std::promise包装的是一个值,都是为了方便异步操作,因为有时需要获取线程中的某个值,这时就用std::promise,而有时需要获一个异步操作的返回值,这时就用std::packaged_task。那么std::promise和std::packaged_task之间又是什么关系呢?可以将一个异步操作的结果保存到std::promise中。

        future被promise和packaged_task用来作为异步操作或者异步结果的连接通道,用std::future和std::shared_future来获取异步调用的结果。future是不可拷贝的,只能移动,shared_future是可以拷贝的,当需要将future放到容器中则需要用shared_future。

        packaged_task和shared_future的基本用法如下所示:


#include <iostream>
#include <utility>
#include <future>
#include <thread>
#include <vector>
using namespace std;

int func(int x)
{
    return x + 2;
}

int add(int x, int y)
{
    return x + y;
}

int main()
{
    std::packaged_task<int(int)> task(func);

    std::future<int> fut = task.get_future();   ///获取future

    std::thread t(std::move(task), 2);          ///将task作为线程函数

    t.detach();

    int value = fut.get();                      ///等待task完成并获取结果

    cout << "The result is " << value << endl;

    vector<std::shared_future<int>> v;

    ///std::future是不能被复制的,不能放到容器中,需要用shared_future
    auto f = std::async(std::launch::async, add, 2, 3);
    ///auto f = std::async(std::launch::async, [](int a, int b){return a + b; }, 2, 3);

    ///v.push_back(f);
    v.push_back(move(f));

    cout << "The shared_future result is " << v[0].get() << endl;

    return 0;
}

线程异步操作函数async

        std::async比std::promise,std:packaged_task和std::thread更高一层,它可以用来直接创建异步的task,异步任务返回的结果也保存在future中,当需要获取异步任务的结果时,只需要调用future.get()方法即可,如果不关注异步任务的结果,只是简单的等待任务完成的话,则调用future.wait()方法。        

        现在看一下std::async的原型(std::launch::async | std::launch::deferred, f,args...),第一个参数是线程的创建策略,两种策略,默认的策略是立即创建线程。

        std::launch::async:在调用async时就开始创建线程。

        std::lauch::deferred:延迟加载方式创建线程。调用async时不创建线程,直到调用了future的get或者wait时才创建线程。

        第二哥参数是线程函数,第三个参数是线程函数的参数。

        std::async的基本用法如下所示:

#include <iostream>
#include <utility>
#include <future>
#include <thread>
#include <vector>
using namespace std;

int func1()
{
    return 8;
}

int func3()
{
    cout << "func3 call....." << endl;
    std::this_thread::sleep_for(std::chrono::seconds(8));
    return 8;
}

int main()
{
    std::future<int> f1 = std::async(std::launch::async, func1);

    cout << f1.get() << endl;

    std::future<int> f2 = std::async(std::launch::async, func1);

    f2.wait();

    std::future<int> f3 = std::async(std::launch::async, func3);

    std::future_status status;

    do
    {
        status = f3.wait_for(std::chrono::seconds(1));
        if (std::future_status::deferred == status)
        {
            cout << "deferred..." << endl;
        }
        else if (std::future_status::timeout == status)
        {
            cout << "timeout..." << endl;
        }
        else if (std::future_status::ready == status)
        {
            cout << "ready..." << endl;
        }
    }while(status != std::future_status::ready);

    cout << "result is " << f3.get() << endl;

    return 0;
}

        可能的结果如下:

8
func3 call.....
timeout...
timeout...
timeout...
timeout...
timeout...
timeout...
timeout...
ready...
result is 8

        std::async是更高层次的异步操作,使得我们不用关注线程创建内部细节,就能方便的获取异步执行状态和结果,还可以指定线程创建策略:应该用std::async替代线程的创建,让它成为我们做异步操作的首选。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值