C++之std::async()

最近忙着秋招,看到了异步、同步的问题,之前确实没怎么看过,记录一下。

首先,使用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对象中可用

参考:c++11多线程编程之std::async的介绍与实例_C 语言_脚本之家 (jb51.net)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值