[编程基础] 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,即:
- std::launch::async。它保证了异步行为,即传递的函数将在单独的线程中执行。
- std::launch::deferred。非异步行为,即当其他线程将来调用get()以访问共享状态时,将调用Function。
- 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/
[编程基础] C++多线程入门9-async教程和示例的更多相关文章
- [编程基础] C++多线程入门4-数据共享和资源竞争
原始C++标准仅支持单线程编程.新的C++标准(称为C++11或C++0x)于2011年发布.在C++11中,引入了新的线程库.因此运行本文程序需要C++至少符合C++ 11标准. 4 数据共享和资源 ...
- [编程基础] C++多线程入门7-条件变量介绍
原始C++标准仅支持单线程编程.新的C++标准(称为C++11或C++0x)于2011年发布.在C++11中,引入了新的线程库.因此运行本文程序需要C++至少符合C++11标准. 文章目录 7 条件变 ...
- [编程基础] C++多线程入门6-事件处理的需求
原始C++标准仅支持单线程编程.新的C++标准(称为C++11或C++0x)于2011年发布.在C++11中,引入了新的线程库.因此运行本文程序需要C++至少符合C++11标准. 文章目录 6 事件处 ...
- [编程基础] C++多线程入门5-使用互斥锁解决资源竞争
原始C++标准仅支持单线程编程.新的C++标准(称为C++11或C++0x)于2011年发布.在C++11中,引入了新的线程库.因此运行本文程序需要C++至少符合C++11标准. 文章目录 5 使用互 ...
- [编程基础] C++多线程入门8-从线程返回值
原始C++标准仅支持单线程编程.新的C++标准(称为C++11或C++0x)于2011年发布.在C++11中,引入了新的线程库.因此运行本文程序需要C++至少符合C++11标准. 8 从线程返回值 8 ...
- [编程基础] C++多线程入门1-创建线程的三种不同方式
原始C++标准仅支持单线程编程.新的C++标准(称为C++11或C++0x)于2011年发布.在C++11中,引入了新的线程库.因此运行本文程序需要C++至少符合C++11标准. 1 创建线程的三种不 ...
- [编程基础] C++多线程入门10-packaged_task示例
原始C++标准仅支持单线程编程.新的C++标准(称为C++11或C++0x)于2011年发布.在C++11中,引入了新的线程库.因此运行本文程序需要C++至少符合C++11标准. 文章目录 10 pa ...
- [编程基础] C++多线程入门3-小心地将参数传递给线程
原始C++标准仅支持单线程编程.新的C++标准(称为c++11或c++0x)于2011年发布.在c++11中,引入了新的线程库.因此运行本文程序需要C++至少符合c++11标准. 文章目录 3 小心地 ...
- [编程基础] C++多线程入门2-连接和分离线程
原始C++标准仅支持单线程编程.新的C++标准(称为C++11或C++0x)于2011年发布.在C++11中,引入了新的线程库.因此运行本文程序需要C++至少符合C++11标准. 文章目录 2 连接和 ...
随机推荐
- LeetCode------找到所有数组中消失的数字(6)【数组】
来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/find-all-numbers-disappeared-in-an-array 1.题目 找到 ...
- 解决办法:ImportError:'module'object has no attribute 'check specifier'
在安装envsubst命令不存在的报错, 安装centos本地源, 再安装gettext) 在指定版本的时候发现还是报错根据后面提示指定44.0.0问题解决 pip install --upgrade ...
- 测试开发jmeter forEach控制器
测试开发jmeter forEach控制器 forEach控制器的使用场景:主要是对大量数据轮询就行接口请求 forEach控制器的使用前提:将数据进行参数化 测试开发jmeter forEach控制 ...
- JavaWeb4
1. Filter 1.1 概述 Filter:过滤器 Servlet.Filter和Listener称为Web的三大组件 生活中的过滤器:净水器.空气净化器.土匪 web中的过滤器:当访问服务器的资 ...
- mybatis一对多根据条件查询的查条数
一对多写了mapper映射之后 根据条件查条数 可以根据主表的唯一id进行分组 在拿到它的count select count(0) over(aa.id),,id,name,age from tab ...
- Java标准类
一个标准的类通常要拥有以下四个组成部分 1.所有的成员变量都要使用private关键字修饰 2.为每一个成员变量编写一对儿Getter/Setter方法 3.编写一个无参数的构造方法 4.编写一个全参 ...
- 树莓派(香橙派)通过.NET IoT 操作SPI编写屏幕驱动 顺手做个四足机器人(一)
摘要 这片文章主要是记录自己的整活过程,涉及到的技术包括.NET IoT, .NET Web, .NET MAUI,框架采用的也是最新的.NET 7. 本人是用的树莓派Zero 2 W(ubuntu- ...
- TreeUtils工具类一行代码实现列表转树【第三版优化】 三级菜单 三级分类 附视频
一.序言 在日常一线开发过程中,总有列表转树的需求,几乎是项目的标配,比方说做多级菜单.多级目录.多级分类等,有没有一种通用且跨项目的解决方式呢?帮助广大技术朋友给业务瘦身,提高开发效率. 本文将基于 ...
- 大前端系统学-了解html
标签: 使用尖括号包起来的就是标签,例如我们看到的 <html></html> 一对标签 <head> 开始标签 </head> 结束标签 < ...
- typora使用CSDN图床
前言 软件下载地址 typora 是一款好用的 markdown 文档编辑器. 我之前研究过CSDN上传图片的接口,前几天发现了Typora 这个好用的软件,可以自定义图床,因为我很多文章图片资源用的 ...