在同步模式下,执行完一个任务后才可以执行下一个任务。而异步任务是将函数调用交给另外一个线程执行,在异步任务执行的同时,原有线程可以继续执行后续的代码,在需要的时候再去获得函数执行的结果。
在c++标准库中有异步函数调用和同步函数调用
async函数(函数执行策略,调用函数,调用函数参数)
下面代码 是用异步任务计算累计的程序
#include<array>
#include<vector>
#include<thread>
#include<iostream>
#include<mutex>
#include<algorithm>
#include<sstream>
#include<iomanip>
#include<semaphore>
#include<syncstream>
#include<numeric>
#include<future>
using namespace std;
float Computer(vector<float>& v)
{
this_thread::sleep_for(chrono::seconds(2));
auto r = accumulate(v.begin(), v.end(), 0.0f);
osyncstream(cout) << "执行任务线程id:" << this_thread::get_id() << endl;
return r;
}
int main()
{
const int RN_MAX = 1000000;
vector<float>numbers(100,0.0f);
generate(numbers.begin(), numbers.end(), [&] {
return 1.0;
});
chrono::steady_clock::time_point begin = chrono::steady_clock::now();
cout << "创建任务线程Id: " << this_thread::get_id() << endl;
future<float>result;
result = async(launch::async,Computer,ref(numbers));
this_thread::sleep_for(chrono::seconds(2));
auto r = result.get();
cout << "result= " << r << endl;
chrono::steady_clock::time_point end = chrono::steady_clock::now();
cout << "任务耗时= " << chrono::duration_cast<chrono::milliseconds>(end - begin).count() << "ms" << endl;
}
输出结果
主函数和Computer函数都设置了两秒的延时,而整个程序执行过程用了两秒,可见主函数和异步函数是并行执行的,除此之外两个线程的id号也不相同。
异步函数调用策略有三种
1.异步策略时相当于新建一个线程或者使用线程池中的一个线程,在线程中 调用一个函数。
2.推迟策略,当future返回一个对象时,只要用于存储函数指针或者对象以及实参的拷贝,当调用get时会调用它存储的这个函数。
3.延迟策略或者异步策略
看下三种策略例子
#include<array>
#include<vector>
#include<thread>
#include<iostream>
#include<mutex>
#include<algorithm>
#include<sstream>
#include<iomanip>
#include<semaphore>
#include<syncstream>
#include<numeric>
#include<future>
#include<map>
using namespace std;
float Computer(vector<float>& v)
{
this_thread::sleep_for(chrono::seconds(2));
auto r = accumulate(v.begin(), v.end(), 0.0f);
osyncstream(cout) << "执行任务线程id:" << this_thread::get_id() << endl;
return r;
}
int main()
{
const int RN_MAX = 1000000;
vector<float>numbers(100, 0.0f);
generate(numbers.begin(), numbers.end(), [&] {
return 1.0;
});
map<string, launch>m =
{
{"默认",(launch)0},
{"异步",launch::async},
{"推迟",launch::deferred},
};
for (auto& p : m)
{
cout << "\n*************************\n策略:" << p.first << endl;
chrono::steady_clock::time_point begin = chrono::steady_clock::now();
cout << "创建任务线程Id: " << this_thread::get_id() << endl;
future<float>result;
if (int(p.second) != 0)
{
if(int(p.second) == 1)
result = async(launch::async, Computer, ref(numbers));
else
{
result = async(launch::deferred, Computer, ref(numbers));
}
}
else
{
result = async(Computer,ref(numbers));
}
this_thread::sleep_for(chrono::seconds(2));
if (result.wait_for(chrono::milliseconds(0)) == future_status::deferred)
{
cout << "任务推迟" << endl;
}
auto r = result.get();
cout << "result= " << r << endl;
chrono::steady_clock::time_point end = chrono::steady_clock::now();
cout << "任务耗时= " << chrono::duration_cast<chrono::milliseconds>(end - begin).count() << "ms" << endl;
}
}
打印结果
可见推迟策略执行任务是在主线程中。
接下来看下打包任务
调用packaged_task对象,我们可以使用成员函数future获得对象,返回的结果会存储future中的共享状态中。
#include<array>
#include<vector>
#include<thread>
#include<iostream>
#include<mutex>
#include<algorithm>
#include<sstream>
#include<iomanip>
#include<semaphore>
#include<syncstream>
#include<numeric>
#include<future>
#include<map>
using namespace std;
float Computer(vector<float>& v)
{
this_thread::sleep_for(chrono::seconds(2));
auto r = accumulate(v.begin(), v.end(), 0.0f);
osyncstream(cout) << "执行任务线程id:" << this_thread::get_id() << endl;
return r;
}
int main()
{
const int RN_MAX = 1000000;
vector<float>numbers(100, 0.0f);
generate(numbers.begin(), numbers.end(), [&] {
return 1.0;
});
cout << "创建任务线程Id: " << this_thread::get_id() << endl;
packaged_task<float(vector<float>&)>task(Computer);
cout << "***********************直接调用********************" << endl;
future<float>result = task.get_future();
task(numbers);
cout << "reuslt=" << result.get() << endl;
task.reset();//重置内部的future对象
cout << "************************使用线程调用***************" << endl;
result = task.get_future();
thread t(move(task),ref(numbers));
cout << "result = " << result.get() << endl;
t.join();
}
通常我们 使用第二种方式将一个打包的任务扔到一个线程中去,这样才能实现真正的优势。