zoukankan      html  css  js  c++  java
  • 【C++11 多线程】async执行异步任务(九)

    一、std::async介绍

    std::async用于创建异步任务,实际上就是创建一个线程异步执行相应任务,它接受回调(即函数或函数对象)作为参数。

    std::async就是异步编程的高级封装,相当于封装了std::promisestd::packaged_task加上std::thread,基本上可以代替std::thread的所有事情。

    template <class Fn, class... Args>
    future<typename result_of<Fn(Args...)>::type> async (launch policy, Fn&& fn, Args&&... args);
    

    std :: async返回一个std :: future ,该值存储由std :: async执行的函数对象返回的值。

    std :: async中的第一个参数是启动策略,它控制std :: async的异步行为。我们可以使用 3 种不同的启动策略来创建,即:

    • std::launch::async:它保证了异步行为,即传递的函数将在单独的线程中执行。
    • std::launch::deferred:非异步行为,即当其他线程将来调用 get() 以访问共享状态时,将调用 Function。
    • std::launch::async | std::launch::deferred:它是默认行为。使用此启动策略,它可以异步运行或不异步运行,具体取决于系统上的负载。但是我们无法控制它。

    二、案例分析

    假设我们必须从数据库中获取一些数据(字符串),并从文件系统中的文件中获取一些数据。然后,我需要合并两个字符串并进行打印。

    在单线程中,我们这样做:

    #include <iostream>
    #include <string>
    #include <chrono>
    #include <thread>
    
    std::string fetchDataFromDB(std::string recvData) {
    	// 确保函数要5秒才能执行完成
    	std::this_thread::sleep_for(std::chrono::seconds(5));
    	// 处理创建数据库连接、获取数据等事情
    	return "DB_" + recvData;
    }
    
    std::string fetchDataFromFile(std::string recvData) {
    	// 确保函数要5秒才能执行完成
    	std::this_thread::sleep_for(std::chrono::seconds(5));
    	// 处理获取文件数据
    	return "File_" + recvData;
    }
    
    int main() {
    	// 获取开始时间
    	std::chrono::system_clock::time_point start = std::chrono::system_clock::now();
    	// 从数据库获取数据
    	std::string dbData = fetchDataFromDB("Data");
    	// 从文件获取数据
    	std::string fileData = fetchDataFromFile("Data");
    	// 获取结束时间
    	auto end = std::chrono::system_clock::now();
    
    	// 计算消耗时间
    	auto diff = std::chrono::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;
    }
    
    /*
    输出:
    Total Time Taken  = 10 Seconds
    Data = DB_Data :: File_Data
    */
    

    由于两个函数fetchDataFromDB()fetchDataFromFile()均需要 5 秒钟,并且都在单个线程中运行,因此,消耗的总时间将为 10 秒钟。

    现在,从数据库和文件中获取数据是彼此独立的,而且非常耗时。因此,我们可以并行运行它们。有两种方法:

    • 一种方式是创建一个新的线程传递一个promise作为线程函数的参数,并在调用线程中从关联的std::future对象获取数据。
    • 另一种简单的方法是使用std::async

    三、使用函数指针调用std::async作为回调

    现在让我们修改上面的代码,并使用std::async异步调用fetchDataFromDB(),即:

    std::future<std::string>resultFromDB = std::async(std::launch::async, fetchDataFromDB, "Data");
    
    std::string dbData = resultDromDB.get()
    

    std::async()做以下事情:

    • 它会自动为我们创建一个线程(或从内部线程池中选择)和一个 promise 对象。
    • 然后将 std :: promise 对象传递给线程函数,并返回关联的 std::future 对象。
    • 当我们传递的参数函数退出时,其值将在此 promise 对象中设置,因此最终返回值将在 std::future 对象中可用。

    现在更改上面的示例,并使用std :: async从数据库异步读取数据,即:

    #include <iostream>
    #include <string>
    #include <chrono>
    #include <thread>
    #include <future>
    
    std::string fetchDataFromDB(std::string recvData) {
    	// 确保函数要5秒才能执行完成
    	std::this_thread::sleep_for(std::chrono::seconds(5));
    	// 处理创建数据库连接、获取数据等事情
    	return "DB_" + recvData;
    }
    
    std::string fetchDataFromFile(std::string recvData) {
    	// 确保函数要5秒才能执行完成
    	std::this_thread::sleep_for(std::chrono::seconds(5));
    	// 处理获取文件数据
    	return "File_" + recvData;
    }
    
    int main() {
    	//获取开始时间
    	std::chrono::system_clock::time_point start = std::chrono::system_clock::now();
    	// 创建异步线程,从数据库获取数据
    	std::future<std::string> resultFromDB = std::async(std::launch::async, fetchDataFromDB, "Data");
    	//从文件获取数据
    	std::string fileData = fetchDataFromFile("Data");
    
    	//从DB获取数据
    	//数据在future<std::string>对象中可获取之前,将一直阻塞
    	std::string dbData = resultFromDB.get();
    	//获取结束时间
    	auto end = std::chrono::system_clock::now();
    
    	// 计算消耗时间
    	auto diff = std::chrono::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;
    }
    /*
    输出:
    Total Time Taken  = 5 Seconds
    Data = DB_Data :: File_Data
    */
    

    因为有一个任务是异步的,在 5 秒内可以同时执行完两个任务。

    四、更多示例

    std::async使用一个 callable 作为一个工作包。在本例中,它可以是个函数、函数对象或者匿名函数

    #include <future>
    #include <iostream>
    
    std::string helloFunction(const std::string& s) {
    	return "Hello C++11 from " + s + ".";
    }
    
    class HelloFunctionObject {
    public:
    	std::string operator()(const std::string& s) const {
    		return "Hello C++11 from " + s + ".";
    	}
    };
    
    int main() {
    	// 带函数的future
    	auto futureFunction = std::async(helloFunction, "function");
    
    	// 带函数对象的future
    	HelloFunctionObject helloFunctionObject;
    	auto futureFunctionObject = std::async(helloFunctionObject, "function object");
    
    	// 带匿名函数的future
    	auto futureLambda = std::async([](const std::string& s) {return "Hello C++11 from " + s + "."; }, "lambda function");
    
    	std::cout << futureFunction.get() << "
    "
    		<< futureFunctionObject.get() << "
    "
    		<< futureLambda.get() << std::endl;
    
    	std::cout << std::endl;
    
    }
    /*
    输出:
    Hello C++11 from function.
    Hello C++11 from function object.
    Hello C++11 from lambda function.
    */
    

    std::async调用在两端的 future 和 promise 创建了一个数据通道。通过 future 的 get() 调用,future 收到了它的工作包的返回值。


    参考:

    C++11 Multithreading – Part 9: std::async Tutorial & Example


  • 相关阅读:
    设置linux session 编码
    在masterpage中添加对usercontrol的引用
    首页的sitecontent地址
    iis的路径
    设置repeater每行多少个的方法
    updatepannel的使用
    取caml查询结果的前多少行
    设置视频自动播放
    网站集与网站
    notepad++ 死机 找到没保存的文件
  • 原文地址:https://www.cnblogs.com/linuxAndMcu/p/14577285.html
Copyright © 2011-2022 走看看