当前位置:   article > 正文

C++ 类模板std::async, std::future, std::promise, std::packaged_task_std::ref std::future

std::ref std::future

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()时函数才开始执行,直到完成,但是使用该枚举类型是,需要注意,他并不是创建一个线程执行调用函数,而是仅仅把函数作为普通的函数调用。我们通过下面这个例子就可以了解到:

  1. #include <iostream>
  2. #include <thread>
  3. #include <vector>
  4. #include <list>
  5. #include <mutex>
  6. #include <memory>
  7. #include <future>
  8. #include <condition_variable>
  9. using namespace std;
  10. class T{
  11. public:
  12. T(){}
  13. ~T(){}
  14. int print(int cnt){
  15. std::cout << "current thread id: " << std::this_thread::get_id() << std::endl;
  16. std::this_thread::sleep_for(std::chrono::milliseconds(1000));
  17. return 666;
  18. }
  19. };
  20. int main(int argc, char* argv[])
  21. {
  22. cout << "main thread = " << std::this_thread::get_id() << endl;
  23. cout << "===================================" << endl << "执行第一个线程" << endl;
  24. T t1;
  25. std::future<int> result1 = std::async(std::launch::async, &T::print, std::ref(t1), 10); //第一个参数使用deferred表示等待后续执行,并且没有创建新线程,延迟执行
  26. cout << "continue ...! " << endl;
  27. cout << "continue ...! " << endl;
  28. cout << "continue ...! " << endl;
  29. cout << "continue ...! " << endl;
  30. cout << "continue ...! " << endl;
  31. cout << "continue ...! " << endl;
  32. cout << "continue ...! " << endl;
  33. cout << result1.get() << endl;
  34. cout << "===================================" << endl << "执行第二个线程" << endl;
  35. T t2;
  36. std::future<int> result2 = std::async(std::launch::deferred, &T::print, std::ref(t2), 10); //第一个参数使用deferred表示等待后续执行,并且没有创建新线程,延迟执行
  37. cout << "continue ...! " << endl;
  38. cout << "continue ...! " << endl;
  39. cout << "continue ...! " << endl;
  40. cout << "continue ...! " << endl;
  41. cout << "continue ...! " << endl;
  42. cout << "continue ...! " << endl;
  43. cout << "continue ...! " << endl;
  44. cout << result2.get() << endl;
  45. cout << "I love China" << endl;
  46. return 0;
  47. }

执行结果:

 

从运行结果就可以看出,当使用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, 通过函数参数获取结果:

  1. #include <iostream>
  2. #include <thread>
  3. #include <vector>
  4. #include <list>
  5. #include <mutex>
  6. #include <memory>
  7. #include <future>
  8. void fun1(std::promise<int> &tmp, int num){
  9. num++;
  10. num *= 10;
  11. tmp.set_value(num); //设置参数返回结果
  12. return;
  13. }
  14. void fun2(std::future<int> &tmpf, int num){
  15. cout << tmpf.get() << endl; //等待线程t1执行完成,并获取参数返回结果
  16. cout << num << endl;
  17. return;
  18. }
  19. int main(){
  20. std::promise<int> myprom; //定义参数返回值对象
  21. std::thread t1(fun1, std::ref(myprom), 666); //线程t1启动
  22. std::future<int> fu1 = myprom.get_future(); //获取t1参数返回值的future对象
  23. std::thread t2(fun2, std::ref(fu1), 999); //线程t2启动
  24. t1.join(); //对t1和t2去join是必须的,否则程序出错
  25. t2.join();
  26. return 0;
  27. }

 

b) 使用std::async,std::future和std::promise, 通过函数参数获取结果:

  1. #include <iostream>
  2. #include <thread>
  3. #include <vector>
  4. #include <list>
  5. #include <mutex>
  6. #include <memory>
  7. #include <future>
  8. void fun1(std::promise<int> &tmp, int num){
  9. num++;
  10. num *= 10;
  11. tmp.set_value(num); //设置参数返回结果
  12. return;
  13. }
  14. void fun2(std::future<int> &tmpf, int num){
  15. cout << tmpf.get() << endl; //等待线程t1执行完成,并获取参数返回结果
  16. cout << num << endl;
  17. return;
  18. }
  19. int main(){
  20. std::promise<int> myprom; //定义参数返回值对象
  21. std::future<void> result1 = std::async(fun1, std::ref(myprom), 666); //线程t1启动
  22. std::future<int> fu1 = myprom.get_future(); //获取t1参数返回值的future对象
  23. std::future<void> result2 = std::async(fun2, std::ref(fu1), 999); //线程t2启动
  24. result1.wait(); //此处wait可以省略,因为在result2中会等待result1线程执行完成并获取参数结果
  25. result2.wait();
  26. return 0;
  27. }

c) 使用std::async,std::future和std::promise, 通过函数返回值和函数参数获取结果:

  1. #include <iostream>
  2. #include <thread>
  3. #include <vector>
  4. #include <list>
  5. #include <mutex>
  6. #include <memory>
  7. #include <future>
  8. int fun1(std::promise<int> &tmp, int num){
  9. num++;
  10. num *= 10;
  11. tmp.set_value(num); //设置参数返回结果
  12. return;
  13. }
  14. int fun2(std::future<int> &tmpf, int num){
  15. cout << tmpf.get() << endl; //等待线程t1执行完成,并获取参数返回结果
  16. cout << num << endl;
  17. return;
  18. }
  19. int main(){
  20. std::promise<int> myprom; //定义参数返回值对象
  21. std::future<int> result1 = std::async(fun1, std::ref(myprom), 666); //线程t1启动
  22. std::future<int> fu1 = myprom.get_future(); //获取t1参数返回值的future对象
  23. std::future<int> result2 = std::async(fun2, std::ref(fu1), 999); //线程t2启动
  24. cout << result1.get() << endl;
  25. cout << result2.get() << endl;
  26. return 0;
  27. }

 

三. 对一个函数进行封装

使用到的模板类有: 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:表示延迟调用

 

代码实现:

  1. #include <iostream>
  2. #include <thread>
  3. #include <vector>
  4. #include <list>
  5. #include <mutex>
  6. #include <memory>
  7. #include <future>
  8. class T{
  9. public:
  10. T(){}
  11. ~T(){}
  12. int print(int cnt){
  13. std::cout << "current thread id: " << std::this_thread::get_id() << std::endl;
  14. std::this_thread::sleep_for(std::chrono::seconds(5));
  15. return 666;
  16. }
  17. };
  18. int main(int argc, char* argv[])
  19. {
  20. cout << "main thread = " << std::this_thread::get_id() << endl;
  21. T t;
  22. std::future<int> result = std::async(std::launch::async, &T::print, std::ref(t), 10); //第一个参数使用deferred表示等待后续执行,并且没有创建新线程,延迟执行
  23. //std::future<int> result = std::async(std::launch::deferred, &T::print, std::ref(t), 10); //延迟调用,主函数调用函数
  24. std::future_status status = result.wait_for(std::chrono::seconds(6)); //超时,程序没有执行完毕
  25. //std::future_status status = result.wait_for(std::chrono::seconds(6)); //线程成功执行完毕,返回
  26. if (status == std::future_status::timeout){
  27. cout << "超时,线程还没有执行完毕" << endl;
  28. }
  29. else if (status == std::future_status::ready){
  30. cout << "线程成功执行完毕,返回" << endl;
  31. cout << result.get() << endl;
  32. }
  33. else if (status == std::future_status::deferred){
  34. cout << "延迟调用,主函数调用函数" << endl;
  35. cout << result.get() << endl;
  36. }
  37. cout << "I love China" << endl;
  38. return 0;
  39. }

 

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/602487
推荐阅读
相关标签
  

闽ICP备14008679号