std::async、std::future创建后台任务并返回值
- 需求:希望线程返回一个结果.
- std::async是个函数模板,用来启动一个异步任务,启动起来一个异步任务之后,他返回一个std::future对象,std::future是个类模板。
- 这个std::future对象里边就含有线程入口函数所返回的结果(线程返回的结果),我们可以通过调用future对象的成员函数get()来获取结果。
- "future": 将来的意思,有人也称呼std::future提供了一种访问异步操作结果的机制,就是说这个结果你可能没有办法马上拿到,但不久的将来,
- 什么叫“启动一个异步任务”,就是自动创建一个线程并开始执行对应的线程入口函数,它返回一个std::future对象。
- 在线程执行完毕的时候,你就能够拿到结果了,所以大家就这么理解:这个future(对象)里会保存一个值,在将来的某个时刻能够拿到。
- 下列程序通过std::future对象的get()成员函数等待线程执行结果并返回结果.
- 这个get()函数很牛,不拿到将来的返回值 誓不罢休,不拿到值我就卡在这里等待拿值。
- get()只能调用一次,不能调用多次。
- wait(),等待线程返回,本身并不返回结果,类似于join()。
#include <iostream>
#include <string>
#include <thread>
#include <vector>
#include <list>
#include <mutex>
#include <future>
using namespace std;
int mythread() //线程入口函数
{
cout << "mythread start" << "ThreadId = " << std::this_thread::get_id() << endl; //打印线程id
std::chrono::milliseconds dura(5000); //休息5秒
std::this_thread::sleep_for(dura);
cout << "mythread end" << "ThreadId = " << std::this_thread::get_id() << endl; //打印线程id
return 100;
}
int main()
{
cout << "MianThreadID = " << std::this_thread::get_id() << endl; //打印主线程id
std::future<int> ret = std::async(mythread); //创建线程并开始执行
//将future对象与async创建的线程绑定到一起,流程不卡在这里,继续运行
std::cout << ret.get() << std::endl; //卡在这里等待mythread执行完毕,拿到结果
//主线程执行
std::cout << "主线程结束" << std::endl;
return 0;
}
【使用类成员函数作为线程入口函数】
#include <iostream>
#include <string>
#include <thread>
#include <vector>
#include <list>
#include <mutex>
#include <future>
using namespace std;
class A{
public:
int a_thread(int var) //线程入口函数
{
cout << var << endl;
cout << "mythread start" << "ThreadId = " << std::this_thread::get_id() << endl; //打印线程id
std::chrono::milliseconds dura(5000); //休息5秒
std::this_thread::sleep_for(dura);
cout << "mythread end" << "ThreadId = " << std::this_thread::get_id() << endl; //打印线程id
return 100;
}
};
int main()
{
A a;
int tmp{666};
cout << "MianThreadID = " << std::this_thread::get_id() << endl; //打印主线程id
std::future<int> ret = std::async(&A::a_thread, &a, tmp); //第二个参数是一个对象引用,才能保证线程里使用的是同一个对象
std::cout << ret.get() << std::endl; //卡在这里等待mythread执行完毕,拿到结果
//主线程执行
std::cout << "主线程结束" << std::endl;
return 0;
}
- 如果上面代码中不使用get函数,结果如下
- 我们通过额外向std::async()传递一个参数,该参数类型是std::lunnch类型(枚举类型)来达到一些特殊的目的。
【std::launch::deferred】
- a) std::launch::deferred: 表示线程入口函数调用被延迟到std::future的wait()或者get()函数调用时才执行。
- 那如果wait()或者get()没有被调用,那么线程会执行吗?没执行,实际上,线程根本就没创建。
- std::launch::deferred: 延迟调用,并且没有创建新线程,是在主线程中调用的线程入口函数。
【std::launch::async】
- std::launch::async,在调用async函数的时候就开始创建线程,即立即执行。
- async()函数,默认用的就是std::launch::async标记。
std::packaged_task(包装各种可调用对象),配合thread类、future类使用
- 是个类模板,它的模板参数是 各种可调用对象;通过std::packaged_task来把各种可调用对象包装起来,方便将来作为线程入口函数来调用。
#include <iostream>
#include <string>
#include <thread>
#include <vector>
#include <list>
#include <mutex>
#include <future>
using namespace std;
int mythread(int var) //线程入口函数
{
cout << var << endl;
cout << "mythread start" << "ThreadId = " << std::this_thread::get_id() << endl; //打印线程id
std::chrono::milliseconds dura(5000); //休息5秒
std::this_thread::sleep_for(dura);
cout << "mythread end" << "ThreadId = " << std::this_thread::get_id() << endl; //打印线程id
return 100;
}
int main()
{
cout << "MianThreadID = " << std::this_thread::get_id() << endl; //打印主线程id
std::packaged_task<int(int)> myTask(mythread); //将函数mythread包装起来
int tmp{ 233 };
std::thread objThread(std::ref(myTask), tmp); //线程直接开始执行,第二个参数作为线程入口函数的参数
objThread.join(); //等待线程执行完毕
std::future<int> ret = myTask.get_future(); //future包含有线程入口函数的返回结果,这里保存mythread的返回结果
cout << ret.get() << endl;
//主线程执行
std::cout << "主线程结束" << std::endl;
return 0;
}
【使用lambda表达式】
#include <iostream>
#include <string>
#include <thread>
#include <vector>
#include <list>
#include <mutex>
#include <future>
using namespace std;
int main()
{
cout << "MianThreadID = " << std::this_thread::get_id() << endl; //打印主线程id
std::packaged_task<int(int)> myTask([](int var) {
cout << var << endl;
cout << "mythread start" << "ThreadId = " << std::this_thread::get_id() << endl; //打印线程id
std::chrono::milliseconds dura(5000); //休息5秒
std::this_thread::sleep_for(dura);
cout << "mythread end" << "ThreadId = " << std::this_thread::get_id() << endl; //打印线程id
return 100;
}); //将lambda表达式包装起来
int tmp{ 233 };
std::thread objThread(std::ref(myTask), tmp); //线程直接开始执行,第二个参数作为线程入口函数的参数
objThread.join(); //等待线程执行完毕
std::future<int> ret = myTask.get_future(); //future包含有线程入口函数的返回结果,这里保存lambda表达式的返回结果
cout << ret.get() << endl;
//主线程执行
std::cout << "主线程结束" << std::endl;
return 0;
}
【packaged_task作为可调用对象直接调用】
- packaged_task包装起来的可调用对象还可以直接调用,所以从这个角度来讲,packaged_task对象,也是一个可调用对象。
#include <iostream>
#include <string>
#include <thread>
#include <vector>
#include <list>
#include <mutex>
#include <future>
using namespace std;
int main()
{
cout << "MianThreadID = " << std::this_thread::get_id() << endl; //打印主线程id
std::packaged_task<int(int)> myTask([](int var) {
cout << var << endl;
cout << "mythread start" << "ThreadId = " << std::this_thread::get_id() << endl; //打印线程id
std::chrono::milliseconds dura(5000); //休息5秒
std::this_thread::sleep_for(dura);
cout << "mythread end" << "ThreadId = " << std::this_thread::get_id() << endl; //打印线程id
return 100;
}); //将lambda表达式包装起来
int tmp{ 233 };
myTask(tmp); //直接调用,相当于直接调用
std::future<int> ret = myTask.get_future(); //future包含有线程入口函数的返回结果,这里保存返回结果
cout << ret.get() << endl;
//主线程执行
std::cout << "主线程结束" << std::endl;
return 0;
}
【结合容器和移动语义使用】
#include <iostream>
#include <string>
#include <thread>
#include <vector>
#include <list>
#include <mutex>
#include <future>
using namespace std;
std::vector<std::packaged_task< int(int)>> TaskVector;
int main()
{
cout << "MianThreadID = " << std::this_thread::get_id() << endl; //打印主线程id
std::packaged_task<int(int)> myTask([](int var) {
cout << var << endl;
cout << "mythread start" << "ThreadId = " << std::this_thread::get_id() << endl; //打印线程id
std::chrono::milliseconds dura(5000); //休息5秒
std::this_thread::sleep_for(dura);
cout << "mythread end" << "ThreadId = " << std::this_thread::get_id() << endl; //打印线程id
return 100;
}); //将lambda表达式包装起来
TaskVector.push_back(std::move(myTask)); //加入至容器,使用了移动语义,进去后myTask就为空
//...
//取出来调用
std::packaged_task< int(int)> myTask2;
auto iter = TaskVector.begin();
myTask2 = std::move(*iter);
TaskVector.erase(iter); //删除第一个元素,之后迭代器就失效了,后面不可以使用iter了
myTask2(72); //直接调用,相当于直接调用
std::future<int> ret = myTask2.get_future(); //future包含有线程入口函数的返回结果,这里保存返回结果
cout << ret.get() << endl;
//主线程执行
std::cout << "主线程结束" << std::endl;
return 0;
}
std::promise
- 我们能够再某个线程中给它赋值,然后我们可以再其他线程中,把这个值取出来用。
#include <iostream>
#include <string>
#include <thread>
#include <vector>
#include <list>
#include <mutex>
#include <future>
using namespace std;
void mythread(std::promise<int>& tmp, int clc)
{
cout << "mythread start" << "ThreadId = " << std::this_thread::get_id() << endl; //打印线程id
//做一系列复杂运算
clc++;
clc *= 233;
//其他运算,比如花费了5秒钟
std::chrono::milliseconds dura(5000); //休息5秒
std::this_thread::sleep_for(dura);
//计算出结果
int ret = clc; //保存结果
tmp.set_value(ret); //结果保存到tmp中
cout << "mythread end" << "ThreadId = " << std::this_thread::get_id() << endl; //打印线程id
}
int main()
{
cout << "MianThreadID = " << std::this_thread::get_id() << endl; //打印主线程id
std::promise<int> var_pro; //创建一个promise对象,用来保存int类型的值
std::thread objThread(mythread, std::ref(var_pro), 10);
//获取结果
objThread.join();
std::future<int> t1 = var_pro.get_future(); //promise和线程绑定,用于获取线程返回值
auto val1 = t1.get();
cout << val1 << endl;
//主线程执行
std::cout << "主线程结束" << std::endl;
return 0;
}
【线程1中返回值,线程2调用(promise + future 起连接作用),实现两个线程之间数据传递】
#include <iostream>
#include <string>
#include <thread>
#include <vector>
#include <list>
#include <mutex>
#include <future>
using namespace std;
void mythread(std::promise<int>& tmp, int clc)
{
cout << "mythread start" << "ThreadId = " << std::this_thread::get_id() << endl; //打印线程id
//做一系列复杂运算
clc++;
clc *= 233;
//其他运算,比如花费了5秒钟
std::chrono::milliseconds dura(5000); //休息5秒
std::this_thread::sleep_for(dura);
//计算出结果
int ret = clc; //保存结果
tmp.set_value(ret); //结果保存到tmp中
cout << "mythread end" << "ThreadId = " << std::this_thread::get_id() << endl; //打印线程id
return;
}
void mythread2(std::future<int>& tmp)
{
cout << "mythread2 start" << "ThreadId = " << std::this_thread::get_id() << endl; //打印线程id
auto val = tmp.get();
cout <<"mythread2 val = " << val << endl;
cout << "mythread2 end" << "ThreadId = " << std::this_thread::get_id() << endl; //打印线程id
return;
}
int main()
{
cout << "MianThreadID = " << std::this_thread::get_id() << endl; //打印主线程id
std::promise<int> var_pro; //创建一个promise对象,用来保存int类型的值
std::thread objThread(mythread, std::ref(var_pro), 10);
//获取结果
objThread.join();
std::future<int> t1 = var_pro.get_future(); //promise和线程绑定,用于获取线程返回值
std::thread objThread2(mythread2, std::ref(t1));
//获取结果
objThread2.join(); //等线程2执行完毕
//主线程执行
std::cout << "主线程结束" << std::endl;
return 0;
}
【小结】
- 这些东西到底怎么用,什么时候用?
- 我们学习这些东西的目的,并不是要把他们都用在咱们自己的实际开发中。
- 相反,如果我们能够用最少的东西能够写出一个稳定、高效的多线程程序,更值得赞赏。
- 我们为了成长,必须要阅读一些高手写的代码,从而快速实现自己代码的积累,我们的技术就会有一个大幅度的提升。