async、future、packaged_task、promise

一、std::async、std::future创建后台任务并返回值

希望线程返回一个结果;
std::async是个函数模板,用来启动一个异步任务,启动起来一个异步任务之后,返回一个std::future对象

“启动一个异步任务”:自动创建一个线程并开始执行对应的线程入口函数,它返回一个std::future对象,
这个future对象里边就有含有线程入口函数锁返回的结果(线程返回的结果),我们可以通过调用future对象的成员函数get()来获取结果;

“future”:将来的意思,有人也称呼std::future提供了一种访问异步操作结果的机制,就是说这个结果你可能没有办法马上拿到,在线程执行完毕的时候,你就能够拿到结果了,所以可以这么理解:这个future对象里会保存一个值,在将来的某个时刻能拿到

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

int mythread()
{
    cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;
    std::chrono::milliseconds dura(5000);
    std::this_thread::sleep_for(dura);
    cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;
    return 5;
}

int main()
{
    cout << "main" << "threadid = " << std::this_thread::get_id() << endl;
    //自动创建一个线程并开始执行对应的线程入口函数,它返回了一个std::future对象
    std::future<int> result = std::async(mythread); //创建一个线程并开始执行,绑定关系;流程并不卡到这里
    cout << "continue......!" << endl;
    int def;
    def = 0;
    cout << result.get() << endl;   //卡在这里等待mythread()执行完毕,拿到结果
    cout << "I Love China!" << endl;
    return 0;
}

上述程序通过std::future对象的get()成员函数等待线程执行结束并返回结果;
这个get()函数很牛,不拿到将来的返回值誓不罢休,不拿到值我就卡在这里就等待拿值

std::future::get()只能调用一次,否则会报异常

std::future::wait()
int main()
{
    cout << "main" << "threadid = " << std::this_thread::get_id() << endl;
    //自动创建一个线程并开始执行对应的线程入口函数,它返回了一个std::future对象
    std::future<int> result = std::async(mythread); //创建一个线程并开始执行,绑定关系;流程并不卡到这里
    cout << "continue......!" << endl;
    int def;
    def = 0;
    // cout << result.get() << endl;   //卡在这里等待mythread()执行完毕,拿到结果
    result.wait();  //等待线程返回,本身并不返回结果
    cout << "I Love China!" << endl;
    return 0;
}
#include <thread>
#include <chrono>
#include <future>
class A
{
public:
    int mythread(int mypar)
    {
        cout << mypar << endl;
        cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;
        std::chrono::milliseconds dura(5000);
        std::this_thread::sleep_for(dura);
        cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;
        return 5;
    }
};


int main()
{
    A a;
    int tmppar = 12;
    cout << "main" << "threadid = " << std::this_thread::get_id() << endl;
    //自动创建一个线程并开始执行对应的线程入口函数,它返回了一个std::future对象
    // std::future<int> result = std::async(mythread); //创建一个线程并开始执行,绑定关系;流程并不卡到这里
    std::future<int> result = std::async(&A::mythread,&a,tmppar);   //第二个参数是对象引用,才能保证线程里用的是同一个对象
    cout << "continue......!" << endl;
    int def;
    def = 0;
    cout << result.get() << endl;   //卡在这里等待mythread()执行完毕,拿到结果
    // result.wait();  //等待线程返回,本身并不返回结果
    cout << "I Love China!" << endl;
    return 0;
}

通过额外向std::async()传递一个参数,该参数的类型是std::launch类型(枚举类型),来达到一些特殊的目的;
a) std::launch::deferred

std::future<int> result = std::async(std::launch::deferred,&A::mythread,&a,tmppar);

表示线程入口函数调用被延迟到std::futurewait或者get()函数调用时才执行;
那如果没有调用wait()或get(),那么线程会执行吗?
答:没执行,实际上,线程根本没有别创建。
std::launch::deferred延迟调用,并且没有创建新线程,是在主线程中调用的线程入口函数(线程ID都一样);

b) std::launch::async

std::future<int> result = std::async(std::launch::async,&A::mythread,&a,tmppar);

在调用async函数的时候就开始创建线程,
只要用了std::launch::async,不需要get()函数,线程就启动起来。

std::launch::asyncstd::launch::deferred不可混合调用,没有延迟。

二、std::packaged_task:

打包任务,把任务包装起来;是个类模板,它的模板参数是各种可调用对象;通过std::packaged_task来把各种可调用对象包装起来,方便将来作为现场入口参数

可以对lambda表达式进行封装:

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

int main()
{
    cout << "main" << "threadid = " << std::this_thread::get_id() << endl;

    std::packaged_task<int(int)> mypt([](int mypar){
        cout << "mythread() start" << "threadid = " << 	std::this_thread::get_id() << endl;
        std::chrono::milliseconds dura(5000);
        std::this_thread::sleep_for(dura);
        cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;
        return 5;
    });    //我们把函数mythread通过packaged_task包装起来
    mypt(105);  //直接调用,相当于函数调用
    std::future<int> result = mypt.get_future();
    cout << result.get() << endl;
    return 0;
}

也可以使用容器来存放packaged_task对象:

vector<std::packaged_task<int(int)>> mytasks;

int main()
{
    cout << "main" << "threadid = " << std::this_thread::get_id() << endl;

    std::packaged_task<int(int)> mypt([](int mypar){
        cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;
        std::chrono::milliseconds dura(5000);
        std::this_thread::sleep_for(dura);
        cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;
        return 5;
    });    //我们把函数mythread通过packaged_task包装起来

    mytasks.push_back(std::move(mypt)); //入容器,这里用了移动语义,入进去之后mypt就为空

    std::packaged_task<int(int)> mypt2;
    auto iter = mytasks.begin();
    mypt2 = std::move(*iter);
    mytasks.erase(iter);    //删除第一个元素,迭代已经失效了,所以后续代码不可以再使用iter

    mypt2(123);
    std::future<int> result = mypt2.get_future();
    cout << result.get() << endl;
    return 0;
   
}

三、std::promise

类模板,我们能够在某个线程中给它赋值,然后可以在其他线程中,把这个值取出来用

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

void mythread(std::promise<int> &tmpp,int calc)
{
    calc++;
    calc *= 10;
    std::chrono::milliseconds dura(5000);
    std::this_thread::sleep_for(dura);

    //终于计算出结果了
    int result = calc;
    tmpp.set_value(result); //结果我保存到了tmpp这个对象中
}


int main()
{
    std::promise<int> myprom;   //声明一个std::promise对象myprm,保存的值类型为int
    std::thread t1(mythread,std::ref(myprom),180);
    t1.join();

    //获取结果值
    std::future<int> fu1 = myprom.get_future(); //promise和future绑定,用于获取线程返回值
    auto result = fu1.get(); //get只能调用一次,不能调用多次
    cout << "result = " << result << endl;
    cout << "I Love China!" << endl;
    return 0;
}

总结:
通过promise保存一个值,再将来某个时刻通过一个future绑定到这个promise上来得到这个绑定的值
注意:
如果用thread对象,一定要用join等,否则程序最终会报异常

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

void mythread(std::promise<int> &tmpp,int calc)
{
    calc++;
    calc *= 10;
    std::chrono::milliseconds dura(5000);
    std::this_thread::sleep_for(dura);

    //终于计算出结果了
    int result = calc;
    tmpp.set_value(result); //结果我保存到了tmpp这个对象中
}

void mythread2(std::future<int> &tmpf)
{
    auto result = tmpf.get();
    cout << "mythread2 result" << result << endl;
    return;
}

int main()
{
    std::promise<int> myprom;   //声明一个std::promise对象myprm,保存的值类型为int
    std::thread t1(mythread,std::ref(myprom),180);
    // t1.join();

    //获取结果值
    std::future<int> fu1 = myprom.get_future(); //promise和future绑定,用于获取线程返回值

    std::thread t2(mythread2,std::ref(fu1));
    t1.join();
    t2.join();

    auto result = fu1.get(); //get只能调用一次,不能调用多次
    cout << "result = " << result << endl;
    cout << "I Love China!" << endl;
    return 0;
}

四、小结

到底怎么用,什么时候用
我们学习这些东西的目的,并不是要把他们都用在自己的实际开发中。
相反,如果我们能够用最少的东西能够写出一个稳定的高效的多线程程序,更值得赞赏。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值