赞
踩
C++ 类模板std::async, std::future, std::promise, std::packaged_task
在C++多线程中常用到各种类模板,今天我们总结一下,下面几种类模板的使用:
1. std::async
2. std::future
3. std::promise
4. std::packaged_task
5. std::future_statue
注:查阅本文档,默认您已经有一定的多线程编程基础,比如熟悉基本的std::thread。
我们介绍是,以使用场景为例:
一. 获取多线程的返回值
二. 从多线程调用的函数参数中获取结果
三. 对一个函数进行封装
四. 当前线程的状态
一、 获取多线程的返回值
使用到的模板类有: std::async, std::future
1. 简单使用示例伪代码:
a) 使用异步函数std::async,调用一个无参数,无返回值的函数,使用std::future::wait进行同步执行。
void fun(){...}
std::future<void> result = std::async(fun); //此处启动线程,类似于std::thread t(fun);后面示例类似
result.wait(); //此处等待线程执行完成,类似于t.join(),后面示例类似
b) 使用异步函数std::async,调用一个有参数,无返回值的函数,使用std::future::wait进行同步执行。
void fun(int tmp){...}
int num = 0;
std::future<void> result = std::async(fun, num);
result.wait();
c) 使用异步函数std::async,调用一个无参数,有返回值的函数,使用std::future::get进行同步执行,获取返回值。当然也可以使用std::future::wait,但是就无法获取返回值了。
int fun(){...}
std::future<int> result = std::async(fun);
int rlt = result.get(); //也可以使用result.wait(),但无法获取返回值
cout << rlt << endl;
d) 使用异步函数std::async,调用一个有参数,有返回值的函数,使用std::future::get进行同步执行,获取返回值。
int fun(int tmp){...}
int num = 0;
std::future<int> result = std::async(fun, num);
int rlt = result.get();
cout << rlt << endl;
e) 使用异步函数std::async,调用一个有参数,有返回值的类方法,使用std::future::get进行同步执行,获取返回值。
class T{
public:
T(){}
~T(){}
int fun(int tmp){...}
};
T t;
int num = 0;
std::future<int> result = std::async(&T::fun, std::ref(t), num); //第一个参数类方法地址,第二个参数类对象的引用,第三个参数类方法的参数。
int rlt = result.get();
cout << rlt << endl;
2. std::async类模板中第一个参数的四种枚举类型
在std::async类模板中第一个参数可以是一个枚举类型,其他的参数位置依次后移。比如:
std::future<int> result = std::async(std::launch::async, &T::fun, std::ref(t), num);
其中std::launch::async为异步执行,即定义该函数后程序就会执行。使用std::future::wait()或者std::future::get()等待线程执行完成。
事实上std::async类模板默认就是使用的std::launch::async类型异步执行,所以当std::async第一个参数未明确定义时,就是使用的std::launch::async类型。即下面两个公式等价:
std::future<int> result = std::async(fun, 666);
std::future<int> result = std::async(std::launch::async, fun, 666)
std::async类模板中的四种枚举类型如下所示:
enum class launch { // names for launch options passed to async
async = 0x1,
deferred = 0x2,
any = async | deferred, // retained
sync = deferred
};
如上面已经将提到的std::launch::async类型,我们仅需要了解std::launch::deferred即可,其他的可以自己尝试。
如果我们使用的类型是std::launch::deferred,则表示使用std::async异步函数时,函数不会立即执行,而是等待使用std::future::wait()或者std::future::get()时函数才开始执行,直到完成,但是使用该枚举类型是,需要注意,他并不是创建一个线程执行调用函数,而是仅仅把函数作为普通的函数调用。我们通过下面这个例子就可以了解到:
- #include <iostream>
- #include <thread>
- #include <vector>
- #include <list>
- #include <mutex>
- #include <memory>
- #include <future>
- #include <condition_variable>
- using namespace std;
-
- class T{
- public:
- T(){}
- ~T(){}
- int print(int cnt){
- std::cout << "current thread id: " << std::this_thread::get_id() << std::endl;
- std::this_thread::sleep_for(std::chrono::milliseconds(1000));
- return 666;
- }
- };
-
- int main(int argc, char* argv[])
- {
- cout << "main thread = " << std::this_thread::get_id() << endl;
- cout << "===================================" << endl << "执行第一个线程" << endl;
- T t1;
- std::future<int> result1 = std::async(std::launch::async, &T::print, std::ref(t1), 10); //第一个参数使用deferred表示等待后续执行,并且没有创建新线程,延迟执行
- cout << "continue ...! " << endl;
- cout << "continue ...! " << endl;
- cout << "continue ...! " << endl;
- cout << "continue ...! " << endl;
- cout << "continue ...! " << endl;
- cout << "continue ...! " << endl;
- cout << "continue ...! " << endl;
- cout << result1.get() << endl;
-
- cout << "===================================" << endl << "执行第二个线程" << endl;
- T t2;
- std::future<int> result2 = std::async(std::launch::deferred, &T::print, std::ref(t2), 10); //第一个参数使用deferred表示等待后续执行,并且没有创建新线程,延迟执行
- cout << "continue ...! " << endl;
- cout << "continue ...! " << endl;
- cout << "continue ...! " << endl;
- cout << "continue ...! " << endl;
- cout << "continue ...! " << endl;
- cout << "continue ...! " << endl;
- cout << "continue ...! " << endl;
- cout << result2.get() << endl;
-
- cout << "I love China" << endl;
-
- return 0;
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
执行结果:
从运行结果就可以看出,当使用std::launch::async类型时,定义异步函数时,程序就已经启动执行了,并且类方法是在新线程中执行的。当时用std::launch::deferred类型时,仅定义了异步函数,程序也并未启动执行,只有当使用std::future::wait()或者std::future::get()时函数才开始执行,并且是作为函数执行的并不是创建了一个多线程,所有就会出现在continue...!执行完之后才执行函数,并且函数的线程号和主线程一样。最后输出I love China.
需要注意的是:std::future::wait()或std::future::get()仅能够调用一次。
二、从多线程调用的函数参数中获取结果
使用到的模板类有: std::thread, std::future, std::promise, std::async
1. 简单使用示例代码
a) 使用std::thread,std::future和std::promise, 通过函数参数获取结果:
- #include <iostream>
- #include <thread>
- #include <vector>
- #include <list>
- #include <mutex>
- #include <memory>
- #include <future>
- void fun1(std::promise<int> &tmp, int num){
- num++;
- num *= 10;
- tmp.set_value(num); //设置参数返回结果
- return;
- }
- void fun2(std::future<int> &tmpf, int num){
- cout << tmpf.get() << endl; //等待线程t1执行完成,并获取参数返回结果
- cout << num << endl;
- return;
- }
- int main(){
- std::promise<int> myprom; //定义参数返回值对象
- std::thread t1(fun1, std::ref(myprom), 666); //线程t1启动
- std::future<int> fu1 = myprom.get_future(); //获取t1参数返回值的future对象
- std::thread t2(fun2, std::ref(fu1), 999); //线程t2启动
- t1.join(); //对t1和t2去join是必须的,否则程序出错
- t2.join();
- return 0;
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
b) 使用std::async,std::future和std::promise, 通过函数参数获取结果:
- #include <iostream>
- #include <thread>
- #include <vector>
- #include <list>
- #include <mutex>
- #include <memory>
- #include <future>
- void fun1(std::promise<int> &tmp, int num){
- num++;
- num *= 10;
- tmp.set_value(num); //设置参数返回结果
- return;
- }
- void fun2(std::future<int> &tmpf, int num){
- cout << tmpf.get() << endl; //等待线程t1执行完成,并获取参数返回结果
- cout << num << endl;
- return;
- }
- int main(){
- std::promise<int> myprom; //定义参数返回值对象
- std::future<void> result1 = std::async(fun1, std::ref(myprom), 666); //线程t1启动
- std::future<int> fu1 = myprom.get_future(); //获取t1参数返回值的future对象
- std::future<void> result2 = std::async(fun2, std::ref(fu1), 999); //线程t2启动
- result1.wait(); //此处wait可以省略,因为在result2中会等待result1线程执行完成并获取参数结果
- result2.wait();
- return 0;
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
c) 使用std::async,std::future和std::promise, 通过函数返回值和函数参数获取结果:
- #include <iostream>
- #include <thread>
- #include <vector>
- #include <list>
- #include <mutex>
- #include <memory>
- #include <future>
- int fun1(std::promise<int> &tmp, int num){
- num++;
- num *= 10;
- tmp.set_value(num); //设置参数返回结果
- return;
- }
- int fun2(std::future<int> &tmpf, int num){
- cout << tmpf.get() << endl; //等待线程t1执行完成,并获取参数返回结果
- cout << num << endl;
- return;
- }
- int main(){
- std::promise<int> myprom; //定义参数返回值对象
- std::future<int> result1 = std::async(fun1, std::ref(myprom), 666); //线程t1启动
- std::future<int> fu1 = myprom.get_future(); //获取t1参数返回值的future对象
- std::future<int> result2 = std::async(fun2, std::ref(fu1), 999); //线程t2启动
- cout << result1.get() << endl;
- cout << result2.get() << endl;
- return 0;
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
三. 对一个函数进行封装
使用到的模板类有: std::thread, std::future, std::async, std::packaged_task
重点:std::packaged_task,打包任务,把任务包装起来,是个类模板,他的模板参数是各种可调用对象,通过std::packaged_task把各种可调用的对象包装起来,方便作为线程入口函数来调用,packaged_task包装起来的对象还可以直接调用,从这个角度说,packaged_task对象也是一个可调用对象。
1. 简单使用示例伪代码
a) 使用std::packaged_task进行封装函数
int fun(int mypar){
cout << mypar << endl;
cout << "mythread() start " << "thread = " << std::this_thread::get_id() << endl;
std::chrono::milliseconds dura(5000);
std::this_thread::sleep_for(dura);
cout << "mythread() end " << "thread = " << std::this_thread::get_id() << endl;
return 5;
}
std::packaged_task<int(int)> mypt(fun); //其中第一个int表示函数fun的返回值,第二个int表示fun的参数。
std::thread t1(std::ref(mypt, 6);
std::future<int> result = mypt.get_future();
cout << result.get() << endl; //此处会等待线程t1执行完成,
t1.join(); //虽然在result.get()中线程t1已经执行完成,但是如果不加join程序仍会报错。
b) 使用std::packaged_task类模板进行lamda表达的封装
std::packaged_task<int(int)> mypt([&]{
cout << mypar << endl;
cout << "mythread() start " << "thread = " << std::this_thread::get_id() << endl;
std::chrono::milliseconds dura(5000);
std::this_thread::sleep_for(dura);
cout << "mythread() end " << "thread = " << std::this_thread::get_id() << endl;
return 5;
}); //其中第一个int表示函数fun的返回值,第二个int表示fun的参数。
std::thread t1(std::ref(mypt, 6);
std::future<int> result = mypt.get_future();
cout << result.get() << endl; //此处会等待线程t1执行完成,
t1.join();
c) 使用std::vector对std::packaged_task类模板进行批量的封装
std::vector<std::packaged_task<int(int)>> tasks; //定义一个队列
std::packaged_task<int(int)> mypt([&]{
cout << mypar << endl;
cout << "mythread() start " << "thread = " << std::this_thread::get_id() << endl;
std::chrono::milliseconds dura(5000);
std::this_thread::sleep_for(dura);
cout << "mythread() end " << "thread = " << std::this_thread::get_id() << endl;
return 5;
}); //其中第一个int表示函数fun的返回值,第二个int表示fun的参数。
tasks.push_back(std::move(mypt)); //必须使用std::move操作把mypt放到tasks中
std::packaged_task<int(int)> mypt2;
auto iter = tasks.begin();
mypt2 = std::move(*iter);
tasks.erase(iter);
mypt2(666); //此处std::packaged_task作为普通函数使用
std::future<int> result = mypt2.get_future(); //使用std::packaged_task::get_future()函数获取future对象,进而获取返回值。
cout << result.get() << endl; //此处获取返回值
四. 当前线程的状态
使用到的模板类有: std::async, std::future, std::future_status
主要讲解std::future_status枚举类型:
enum class future_status { // names for timed wait function returns
ready,
timeout,
deferred
};
a) ready:表示指定时间内线程已经执行完成
b) timeout:表示指定时间内线程并没有完成
c) deferred:表示延迟调用
代码实现:
- #include <iostream>
- #include <thread>
- #include <vector>
- #include <list>
- #include <mutex>
- #include <memory>
- #include <future>
- class T{
- public:
- T(){}
- ~T(){}
- int print(int cnt){
- std::cout << "current thread id: " << std::this_thread::get_id() << std::endl;
- std::this_thread::sleep_for(std::chrono::seconds(5));
- return 666;
- }
- };
-
- int main(int argc, char* argv[])
- {
- cout << "main thread = " << std::this_thread::get_id() << endl;
- T t;
- std::future<int> result = std::async(std::launch::async, &T::print, std::ref(t), 10); //第一个参数使用deferred表示等待后续执行,并且没有创建新线程,延迟执行
- //std::future<int> result = std::async(std::launch::deferred, &T::print, std::ref(t), 10); //延迟调用,主函数调用函数
-
- std::future_status status = result.wait_for(std::chrono::seconds(6)); //超时,程序没有执行完毕
- //std::future_status status = result.wait_for(std::chrono::seconds(6)); //线程成功执行完毕,返回
- if (status == std::future_status::timeout){
- cout << "超时,线程还没有执行完毕" << endl;
- }
- else if (status == std::future_status::ready){
- cout << "线程成功执行完毕,返回" << endl;
- cout << result.get() << endl;
- }
- else if (status == std::future_status::deferred){
- cout << "延迟调用,主函数调用函数" << endl;
- cout << result.get() << endl;
- }
-
- cout << "I love China" << endl;
- return 0;
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。