最近忙着秋招,看到了异步、同步的问题,之前确实没怎么看过,记录一下。
首先,使用async的方法可以实现异步,其原型函数如下:
template<class Fn, class... Args>
future<typename result_of<Fn(Args...)>::type> async(launch policy, Fn&& fn, Args&&...args);
参数:
第一个参数是执行的策略,即立刻执行还是等一会再执行(也就是等待调用get()方法才执行),即同步还是异步。分成三种情况如下:
1.std::launch::async 传递的可调用对象异步执行;
2.std::launch::deferred 传递的可调用对象同步执行;
3.std::launch::async | std::launch::deferred 是将同步还是异步的选择权交给操作系统,由操作系统去抉择。同时这也是默认选项(不指定策略)
第二个参数是执行的内容,可以是一个函数对象,也可以是lambda表达式等。
执行结果:
可以使用get、wait、wait_for、wait_until等待执行结果,其中get可以获得执行的结果。
如果选择异步执行策略,调用get时,如果异步执行没有结束,get会阻塞当前调用线程,直到异步执行结束并获得结果,如果异步执行已经结束,不等待获取执行结果;如果选择同步执行策略,只有当调用get函数时,同步调用才真正执行,这也被称为函数调用被延迟。
返回值:
返回一个 std::future<T>,也就是feature的状态:
1.deffered:异步操作还没有开始;
2.ready:异步操作已经完成;
3.timeout:异步操作超时。
代码示例:
假设我们必须从数据库和文件系统里里获取一些数据(字符串),然后需要合并字符串并打印。
如果不使用async:
#include <iostream>
#include <string>
#include <chrono>
#include <thread>
using namespace std::chrono;
std::string fetchDataFromDB(std::string recvData) {
//确保函数要5秒才能执行完成
std::this_thread::sleep_for(seconds(5));
//处理创建数据库连接、获取数据等事情
return "DB_" + recvData;
}
std::string fetchDataFromFile(std::string recvData) {
//确保函数要5秒才能执行完成
std::this_thread::sleep_for(seconds(5));
//处理获取文件数据
return "File_" + recvData;
}
int main() {
//获取开始时间
system_clock::time_point start = system_clock::now();
//从数据库获取数据
std::string dbData = fetchDataFromDB("Data");
//从文件获取数据
std::string fileData = fetchDataFromFile("Data");
//获取结束时间
auto end = system_clock::now();
auto diff = duration_cast<std::chrono::seconds>(end - start).count();
std::cout << "Total Time taken= " << diff << "Seconds" << std::endl;
//组装数据
std::string data = dbData + " :: " + fileData;
//输出组装的数据
std::cout << "Data = " << data << std::endl;
return 0;
}
获取这个字符串并打印需要10s,但既然从数据库和文件系统中获取数据是独立的并且都要耗时,那我们可以并行地运行他们。即可以使用async。
同步执行和异步执行:
#include <iostream>
#include <string>
#include <chrono>
#include <thread>
#include <future>
using namespace std::chrono;
std::string fetchDataFromDB(std::string recvData) {
std::cout << "fetchDataFromDB start" << std::this_thread::get_id() << std::endl;
std::this_thread::sleep_for(seconds(5));
return "DB_" + recvData;
}
std::string fetchDataFromFile(std::string recvData) {
std::cout << "fetchDataFromFile start" << std::this_thread::get_id() << std::endl;
std::this_thread::sleep_for(seconds(3));
return "File_" + recvData;
}
int main() {
std::cout << "main start" << std::this_thread::get_id() << std::endl;
//获取开始时间
system_clock::time_point start = system_clock::now();
std::future<std::string> resultFromDB = std::async(std::launch::async, fetchDataFromDB, "Data");
//从文件获取数据
std::future<std::string> fileData = std::async(std::launch::deferred, fetchDataFromFile, "Data");
//如果fetchDataFromDB()执行没有完成,get会一直阻塞当前线程
std::string dbData = resultFromDB.get();
//直到调用get函数fetchDataFromFile才开始执行
std::string FileData = fileData.get();
//获取结束时间
auto end = system_clock::now();
auto diff = duration_cast<std::chrono::seconds>(end - start).count();
std::cout << "Total Time taken= " << diff << "Seconds" << std::endl;
//组装数据
std::string data = dbData + " :: " + FileData;
//输出组装的数据
std::cout << "Data = " << data << std::endl;
return 0;
}
(可以试试调换代码中异步和同步get的顺序,可以明显发现在执行异步时,如果没有执行结束,get会阻塞当前调用线程,直到有结果,而同步的话,只有在调用get时,才开始执行)
查询future的状态获取异步执行的结果:
#include <iostream>
#include <string>
#include <chrono>
#include <thread>
#include <future>
using namespace std::chrono;
std::string fetchDataFromDB(std::string recvData) {
std::cout << "fetchDataFromDB start" << std::this_thread::get_id() << std::endl;
std::this_thread::sleep_for(seconds(5));
return "DB_" + recvData;
}
int main() {
std::cout << "main start" << std::this_thread::get_id() << std::endl;
//获取开始时间
system_clock::time_point start = system_clock::now();
std::future<std::string> resultFromDB = std::async(std::launch::async, fetchDataFromDB, "Data");
std::future_status status;
std::string dbData;
do
{
status = resultFromDB.wait_for(std::chrono::seconds(1));
switch (status)
{
case std::future_status::ready:
std::cout << "Ready..." << std::endl;
//获取结果
dbData = resultFromDB.get();
std::cout << dbData << std::endl;
break;
case std::future_status::timeout:
std::cout << "timeout..." << std::endl;
break;
case std::future_status::deferred:
std::cout << "deferred..." << std::endl;
break;
default:
break;
}
} while (status != std::future_status::ready);
//获取结束时间
auto end = system_clock::now();
auto diff = duration_cast<std::chrono::seconds>(end - start).count();
std::cout << "Total Time taken= " << diff << "Seconds" << std::endl;
return 0;
}
运行结果:
main start16096
fetchDataFromDB start16964
timeout...
timeout...
timeout...
timeout...
Ready...
DB_Data
Total Time taken= 5Seconds
总结:
std::async()做如下的事情:
1.自动创建一个线程(或从内部线程池中挑选)和一个promise对象。
2.然后将std::promise对象传递给线程函数,并返回相关的std::future对象
3.当我们传递参数的函数退出时,它的值将被设置在这个promise对象中,所以最终的返回值将在std::future对象中可用