并发多线程9async、future、packaged_task、promise

第九节、async、future、packaged_task、promise

本节内容需要包含头文件#include <future>

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

std::async是一个函数模板,用来启动一个异步任务,启动起来一个异步任务之后,它返回一个std::future对象,这个对象是个类模板。

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

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

std::future对象的get()成员函数会等待线程执行结束并返回结果,拿不到结果它就会一直等待,感觉有点像join()。但是,它是可以获取结果的。

std::future对象的wait()成员函数,用于等待线程返回,本身并不返回结果,这个效果和 std::thread 的join()更像。

#include <iostream>
#include <future>
using namespace std;
class A {
public:
    int mythread(int mypar) {
        cout << mypar << endl;
        return mypar;
    }
};


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() {
    A a;
    int tmp = 12;
    cout << "main" << "threadid = " << std::this_thread::get_id() << endl;
    std::future<int> result1 = std::async(mythread);
    cout << "continue........" << endl;
    cout << result1.get() << endl; //卡在这里等待mythread()执行完毕,拿到结果

    //类成员函数
    std::future<int> result2 = std::async(&A::mythread, &a, tmp); //第二个参数是对象引用才能保证线程里执行的是同一个对象
    cout << result2.get() << endl;
   //或者result2.wait();
    cout << "good luck" << endl;
    return 0;
}

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

 

1、std::lunch::deferred:

(defer推迟,延期)表示线程入口函数的调用会被延迟,一直到std::future的wait()或者get()函数被调用时(由主线程调用)才会执行;如果wait()或者get()没有被调用,则不会执行。
实际上根本就没有创建新线程。std::launch::deferred意思时延迟调用,并没有创建新线程,是在主线程中调用的线程入口函数。

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

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<int> result1 = std::async(std::launch::deferred ,mythread);
    cout << "continue........" << endl;
    cout << result1.get() << endl; //卡在这里等待mythread()执行完毕,拿到结果
    cout << "good luck" << endl;
    return 0;
}

永远都会先打印出continue…,然后才会打印出mythread() start和mythread() end等信息。

2、std::launch::async,在调用async函数的时候就开始创建新线程

int main() {
    cout << "main" << "threadid = " << std::this_thread::get_id() << endl;
    std::future<int> result1 = std::async(std::launch::async ,mythread);
    cout << "continue........" << endl;
    cout << result1.get() << endl; 
    cout << "good luck" << endl;
    return 0;
}

二、std::packaged_task:打包任务,把任务包装起来。

类模板,它的模板参数是各种可调用对象,通过packaged_task把各种可调用对象包装起来,方便将来作为线程入口函数来调用。

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

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() {
    cout << "main" << "threadid = " << std::this_thread::get_id() << endl;
    //我们把函数mythread通过packaged_task包装起来
    //参数是一个int,返回值类型是int
    std::packaged_task<int(int)> mypt(mythread);
    std::thread t1(std::ref(mypt), 1);
    t1.join();
    std::future<int> result = mypt.get_future(); 
    //std::future对象里包含有线程入口函数的返回结果,这里result保存mythread返回的结果。
    cout << result.get() << endl;

    return 0;
}

可调用对象可由函数换成lambda表达式

int main() {
    cout << "main" << "threadid = " << std::this_thread::get_id() << endl;
    std::packaged_task<int(int)> mypt([](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;
    }); 

    std::thread t1(std::ref(mypt), 1);
    t1.join();
    std::future<int> result = mypt.get_future(); 
    //std::future对象里包含有线程入口函数的返回结果,这里result保存mythread返回的结果。
    
    cout << result.get() << endl;

    cout << "good luck" << endl;
    return 0;
}

packaged_task包装起来的可调用对象还可以直接调用,从这个角度来讲,packaged_task对象也是一个可调用对象
lambda的直接调用

int main() {
    cout << "main" << "threadid = " << std::this_thread::get_id() << endl;
    std::packaged_task<int(int)> mypt([](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;
    }); 

    mypt(1);
    std::future<int> result = mypt.get_future();
    cout << result.get() << endl;
}

std::promise,类模板

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

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

void mythread(std::promise<int> &tmp, int clac) {
    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;
    int result = clac;
    tmp.set_value(result); //结果保存到了tmp这个对象中
    return;
}

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

int main() {
    std::promise<int> myprom;
    std::thread t1(mythread, std::ref(myprom), 180);
    t1.join(); //在这里线程已经执行完了
    std::future<int> fu1 = myprom.get_future(); //promise和future绑定,用于获取线程返回值
    auto result = fu1.get();
    cout << "result = " << result << endl;
}

总结:通过promise保存一个值,在将来某个时刻我们通过把一个future绑定到这个promise上,来得到绑定的值

注意:使用thread时,必须 join() 或者 detach() 否则程序会报异常

小结:

我们学习这些东西的目的并不是,要把他们都用到实际开发中。

相反,如果我们能够用最少的东西写出一个稳定的,高效的多线程程序,更值得赞赏。

我们为了成长必须阅读一些高手写的代码,从而实现自己代码的积累;

原文链接:https://blog.csdn.net/qq_38231713/article/details/106092879

原文链接: https://www.cnblogs.com/gk520/p/16644490.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    并发多线程9async、future、packaged_task、promise

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/404287

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年4月25日 下午4:27
下一篇 2023年4月25日 下午4:27

相关推荐