本文系列大部分来自c++11并发与多线程视频课程的学习笔记,系列文章有(不定期更新维护):
- C++并发与多线程(一)线程传参
- C++并发与多线程(二) 创建多个线程、数据共享问题分析、案例代码
- C++并发与多线程(三)单例设计模式与共享数据分析、call_once、condition_variable使用
- C++并发与多线程(四)async、future、packaged_task、promise、shared_future
- C++并发与多线程(五)互斥量,atomic、与线程池
std::async、std::future创建后台任务并返回值
之前,我们用std::thread
创建一个线程,用join()
等待这个线程结束,如果希望线程返回一个结果呢?
std::async
是一个函数模板,用来启动一个异步任务,启动起来一个异步任务之后,它返回一个std::future
对象,这个对象也是个类模板。什么叫“启动一个异步任务”?就是自动创建一个线程,并开始 执行对应的线程入口函数,它返回一个std::future
对象,这个std::future
对象中就含有线程入口函数所返回的结果,我们可以通过调用future
对象的成员函数get()
来获取结果。
“future”
将来的意思,也有人称呼std::future
提供了一种访问异步操作结果的机制,就是说这个结果你可能没办法马上拿到,但是在不久的将来,这个线程执行完毕的时候,你就能够拿到结果了,所以,大家这么理解:future
中保存着一个值,这个值是在将来的某个时刻能够拿到。
#include <iostream> #include <future> using namespace std; int mythread(){ cout << "mythread() started and the thread id is " << std::this_thread::get_id() << endl; std::chrono::milliseconds dura(5000); // 休息五秒 std::this_thread::sleep_for(dura); cout << "mythread() ended and the thread id is " << std::this_thread::get_id() << endl; return 5; } int main(){ cout << "main started and the thread id is " << std::this_thread::get_id() << endl; std::future<int> res = std::async(mythread); // 创建一个线程并开始执行 cout << "continue ....." << endl; cout << "res.get() is : " << res.get() << endl; // 执行到get的时候,会卡在此行,等待mythread执行完毕。 cout << "main ended and the thread id is " << std::this_thread::get_id() << endl; }
程序输出结果为:
main started and the thread id is 0x1000e3d40 continue ..... res.get() is : mythread() started and the thread id is 0x16fe87000 mythread() ended and the thread id is 0x16fe87000 5 main ended and the thread id is 0x1000e3d40
std::future
对象的get()
成员函数会等待线程执行结束并返回结果,拿不到结果它就会一直等待,感觉有点像join()
但是,它是可以获取结果的。而std::future
对象的wait()
成员函数,用于等待线程返回,本身并不返回结果,这个效果和std::thread
的join()
更像。
#include <iostream> #include <future> using namespace std; int mythread(){ cout << "mythread() started and the thread id is " << std::this_thread::get_id() << endl; std::chrono::milliseconds dura(5000); // 休息五秒 std::this_thread::sleep_for(dura); cout << "mythread() ended and the thread id is " << std::this_thread::get_id() << endl; return 5; } int main(){ cout << "main started and the thread id is " << std::this_thread::get_id() << endl; std::future<int> res = std::async(mythread); // 创建一个线程并开始执行 cout << "continue ....." << endl; //cout << "res.get() is : " << res.get() << endl; // 执行到get的时候,会卡在此行,等待mythread执行完毕。 res.wait(); // 等待线程返回,但是拿不到返回值,类似join。 cout << "main ended and the thread id is " << std::this_thread::get_id() << endl; }
同样的,我们可以使用类成员函数作为线程的入口函数:
#include <iostream> #include <future> using namespace std; class A{ public: int mythread(int num){ cout << "mythread() started and the thread id is " << std::this_thread::get_id() << endl; cout << "num is: " << num << endl; std::chrono::milliseconds dura(5000); // 休息五秒 std::this_thread::sleep_for(dura); cout << "mythread() ended and the thread id is " << std::this_thread::get_id() << endl; return 5; } }; int main(){ A a; cout << "main started and the thread id is " << std::this_thread::get_id() << endl; std::future<int> res = std::async(&A::mythread, &a, 10); // 第二个参数是对象引用,如果不用引用的话,就会创建一个新的类A的对象。 cout << "continue ....." << endl; cout << "res.get() is : " << res.get() << endl; // 执行到get的时候,会卡在此行,等待mythread执行完毕。 cout << "main ended and the thread id is " << std::this_thread::get_id() << endl; }
我们可以通过向std::async()
额外传递一个参数,该参数是std::launch
类型(枚举类型),来达到一些特殊的目的:
std::lunch::deferred
:(defer
推迟,延期)表示线程入口函数的调用会被延迟,一直到std::future
的wait()
或者get()
函数被调用时(由主线程调用)才会执行;如果wait()
或者get()
没有被调用,则不会执行。
#include <iostream> #include <future> using namespace std; class A{ public: int mythread(int num){ cout << "mythread() started and the thread id is " << std::this_thread::get_id() << endl; cout << "num is: " << num << endl; std::chrono::milliseconds dura(5000); // 休息五秒 std::this_thread::sleep_for(dura); cout << "mythread() ended and the thread id is " << std::this_thread::get_id() << endl; return 5; } }; int main(){ A a; cout << "main started and the thread id is " << std::this_thread::get_id() << endl; std::future<int> res = std::async(std::launch::deferred,&A::mythread, &a, 10); // 第二个参数是对象引用,如果不用引用的话,就会创建一个新的类A的对象。 cout << "continue ....." << endl; cout << "res.get() is : " << res.get() << endl; // 执行到get的时候,会卡在此行,等待mythread执行完毕。 cout << "main ended and the thread id is " << std::this_thread::get_id() << endl; }
程序输出结果为:
main started and the thread id is 0x1000e7d40 continue ..... res.get() is : mythread() started and the thread id is 0x1000e7d40 num is: 10 mythread() ended and the thread id is 0x1000e7d40 5 main ended and the thread id is 0x1000e7d40 Program ended with exit code: 0
可以看到主线程id
为0x1000e7d40
,子线程id
同样为0x1000e7d40
,也就是说,实际上根本就没有创建新线程。std::lunch::deferred
意思时延迟调用,并没有创建新线程,是在主线程中调用的线程入口函数。上述代码永远都会先打印出continue…
,然后才会打印出mythread() start
和mythread() end
等信息。
std::launch::async
,在调用async
函数的时候就开始创建新线程。
#include <iostream> #include <future> using namespace std; class A{ public: int mythread(int num){ cout << "mythread() started and the thread id is " << std::this_thread::get_id() << endl; cout << "num is: " << num << endl; std::chrono::milliseconds dura(5000); // 休息五秒 std::this_thread::sleep_for(dura); cout << "mythread() ended and the thread id is " << std::this_thread::get_id() << endl; return 5; } }; int main(){ A a; cout << "main started and the thread id is " << std::this_thread::get_id() << endl; std::future<int> res = std::async(std::launch::async,&A::mythread, &a, 10); // 第二个参数是对象引用,如果不用引用的话,就会创建一个新的类A的对象。 cout << "continue ....." << endl; cout << "res.get() is : " << res.get() << endl; // 执行到get的时候,会卡在此行,等待mythread执行完毕。 cout << "main ended and the thread id is " << std::this_thread::get_id() << endl; }
程序输出结果为:
main started and the thread id is 0x1000e7d40 continue ..... res.get() is : mythread() started and the thread id is 0x16fe87000 num is: 10 mythread() ended and the thread id is 0x16fe87000 5 main ended and the thread id is 0x1000e7d40 Program ended with exit code: 0
std::packaged_task
std::packaged_task
打包任务,把任务包装起来。也是一个类模板,它的模板参数是各种可调用对象,通过packaged_task
把各种可调用对象包装起来,方便将来作为线程入口函数来调用。
#include <iostream> #include <future> using namespace std; int mythread(int num){ cout << "mythread() started and the thread id is " << std::this_thread::get_id() << endl; cout << "num is: " << num << endl; std::chrono::milliseconds dura(5000); // 休息五秒 std::this_thread::sleep_for(dura); cout << "mythread() ended and the thread id is " << std::this_thread::get_id() << endl; return 5; } int main(){ cout << "main started and the thread id is " << std::this_thread::get_id() << endl; //我们把函数mythread通过packaged_task包装起来。参数是一个int,返回值类型是int std::packaged_task<int(int)> mypt(mythread); std::thread mythread(std::ref(mypt), 10); // 线程直接开始执行,第二个参数为线程入口参数 mythread.join(); //std::future对象里包含有线程入口函数的返回结果,这里result保存mythread返回的结果。 std::future<int> res = mypt.get_future(); cout << "res.get() is : " << res.get() << endl; cout << "main ended and the thread id is " << std::this_thread::get_id() << endl; }
输出结果为:
main started and the thread id is 0x1000e3d40 mythread() started and the thread id is 0x16fe87000 num is: 10 mythread() ended and the thread id is 0x16fe87000 res.get() is : 5 main ended and the thread id is 0x1000e3d40 Program ended with exit code: 0
可调用对象可由函数换成lambda
表达式:
#include <iostream> #include <future> using namespace std; int main(){ cout << "main started and the thread id is " << std::this_thread::get_id() << endl; //我们把函数mythread通过packaged_task包装起来。参数是一个int,返回值类型是int std::packaged_task<int(int)> mypt([](int num){ cout << "mythread() started and the thread id is " << std::this_thread::get_id() << endl; cout << "num is: " << num << endl; std::chrono::milliseconds dura(5000); // 休息五秒 std::this_thread::sleep_for(dura); cout << "mythread() ended and the thread id is " << std::this_thread::get_id() << endl; return 5; }); std::thread mythread(std::ref(mypt), 10); // 线程直接开始执行,第二个参数为线程入口参数 mythread.join(); //std::future对象里包含有线程入口函数的返回结果,这里result保存mythread返回的结果。 std::future<int> res = mypt.get_future(); cout << "res.get() is : " << res.get() << endl; cout << "main ended and the thread id is " << std::this_thread::get_id() << endl; }
输出结果为:
main started and the thread id is 0x1000e3d40 mythread() started and the thread id is 0x16fe87000 num is: 10 mythread() ended and the thread id is 0x16fe87000 res.get() is : 5 main ended and the thread id is 0x1000e3d40 Program ended with exit code: 0