[编程基础] C++多线程入门9-async教程和示例

原始C++标准仅支持单线程编程。新的C++标准(称为C++11或C++0x)于2011年发布。在C++11中,引入了新的线程库。因此运行本文程序需要C++至少符合C++11标准。

9 async教程和示例

在本文中,我们将讨论如何在C++11中使用std::async异步执行任务。std::async在c++11中引入。

9.1 什么是std::async()

std::async()是一个函数模板,它接受回调(即函数或函数对象)作为参数,并有可能异步执行它们。

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

std::async返回一个std::future<T>,该值存储由std::async()执行的函数对象返回的值。函数期望的参数可以在函数指针参数之后作为参数传递给std::async()。
std::async中的第一个参数是启动策略,它控制std::async的异步行为。我们可以使用3种不同的启动策略创建std::async,即:

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

如果我们不指定启动策略。它的行为类似于std::launch::async | std::launch::deferred。
在本文中,我们将使用std::launch::async启动策略。

我们可以在std::async中传递任何回调,即

  • 函数指针
  • 函数对象
  • Lambda函数
    让我们通过一个例子来了解std::async的需求。

9.2 需要std::async()

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

#include <iostream>
#include <string>
#include <chrono>
#include <thread>
using namespace std::chrono;
std::string fetchDataFromDB(std::string recvdData)
{
	// Make sure that function takes 5 seconds to complete
	// 等待五秒
	std::this_thread::sleep_for(seconds(5));
	// Do stuff like creating DB Connection and fetching Data
	// 做一些事情,比如创建数据库连接和获取数据
	return "DB_" + recvdData;
}

std::string fetchDataFromFile(std::string recvdData)
{
	// Make sure that function takes 5 seconds to complete
	std::this_thread::sleep_for(seconds(5));
	// Do stuff like fetching Data File
	// 获取数据
	return "File_" + recvdData;
}
int main()
{
	// Get Start Time
	// 获得开始时间
	system_clock::time_point start = system_clock::now();
	// Fetch Data from DB
	// 从数据库中获得数据
	std::string dbData = fetchDataFromDB("Data");
	// Fetch Data from File
	// 从文件中获得数据
	std::string fileData = fetchDataFromFile("Data");
	// Get End Time
	auto end = system_clock::now();
	// 获得运行时间
	auto diff = duration_cast <std::chrono::seconds> (end - start).count();
	std::cout << "Total Time Taken = " << diff << " Seconds" << std::endl;
	// Combine The Data
	// 组合数据
	std::string data = dbData + "::" + fileData;
	// Printing the combined Data
	// 打印数据
	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。

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

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

std::future<std::string> resultFromDB = std::async(std::launch::async, fetchDataFromDB, "Data");
// Do Some Stuff 
//Fetch Data from DB
// Will block till data is available in future<std::string> object.
std::string dbData = resultFromDB.get();

std::async()做以下事情,它会自动为我们创建一个线程(或从内部线程池中选择)和一个​​promise对象。然后将std::promise对象传递给线程函数,并返回关联的std::future对象。当我们传递的参数函数退出时,其值将在此promise对象中设置,因此最终返回值将在std::future对象中可用。现在更改上面的示例,并使用std::async从数据库异步读取数据,即

#include <iostream>
#include <string>
#include <chrono>
#include <thread>
#include <future>
using namespace std::chrono;
std::string fetchDataFromDB(std::string recvdData)
{
	// Make sure that function takes 5 seconds to complete
	std::this_thread::sleep_for(seconds(5));
	//Do stuff like creating DB Connection and fetching Data
	return "DB_" + recvdData;
}
std::string fetchDataFromFile(std::string recvdData)
{
	// Make sure that function takes 5 seconds to complete
	std::this_thread::sleep_for(seconds(5));
	//Do stuff like fetching Data File
	return "File_" + recvdData;
}
int main()
{
	// Get Start Time
	system_clock::time_point start = system_clock::now();
	// 异步执行
	std::future<std::string> resultFromDB = std::async(std::launch::async, fetchDataFromDB, "Data");
	//Fetch Data from File
	std::string fileData = fetchDataFromFile("Data");
	//Fetch Data from DB
	// Will block till data is available in future<std::string> object.
	std::string dbData = resultFromDB.get();
	// Get End Time
	auto end = system_clock::now();
	auto diff = duration_cast <std::chrono::seconds> (end - start).count();
	std::cout << "Total Time Taken = " << diff << " Seconds" << std::endl;
	//Combine The Data
	std::string data = dbData + "::" + fileData;
	//Printing the combined Data
	std::cout << "Data = " << data << std::endl;
	return 0;
}

输出为:

Total Time Taken = 5 Seconds
Data = DB_Data::File_Data

现在只需要5秒钟,便可执行完程序。
此外我们还有两种方式实现同样的功能

使用函数对象作为回调调用std::async

/*
* Function Object
*/
struct DataFetcher
{
	std::string operator()(std::string recvdData)
	{
		// Make sure that function takes 5 seconds to complete
		std::this_thread::sleep_for(seconds(5));
		//Do stuff like fetching Data File
		return "File_" + recvdData;
	}
};
//Calling std::async with function object
std::future<std::string> fileResult = std::async(DataFetcher(), "Data");

使用Lambda函数作为回调调用std::async

//Calling std::async with lambda function
std::future<std::string> resultFromDB = std::async([](std::string recvdData) {
	std::this_thread::sleep_for(seconds(5));
	//Do stuff like creating DB Connection and fetching Data
	return "DB_" + recvdData;
}, "Data");

9.4 参考

https://thispointer.com/c11-multithreading-part-9-stdasync-tutorial-example/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值