在 C++11 中,std::async
是一个非常有用的标准库功能,它不仅可以用来执行 异步任务 ,即在后台线程中执行某些操作,同时也可以用于 延迟任务,即任务只有在需要结果时才会执行。
- 异步任务:可以并发执行的任务,它们之间相互独立,不需要等待彼此完成。异步任务适合计算量较大、耗时较长的任务,比如大规模数据处理、复杂的数学计算等任务。
- 延迟任务:任务的执行被推迟,直到真正需要结果时才开始执行。延迟任务适合一些需要推迟执行的业务场景,比如:系统负载较高、结果又不急需,提升系统性能。
- 异步任务 {#title-0} ==================
1.1 异步任务的基本使用 {#title-1}
#if 1
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<thread>
#include<chrono>
#include<future>
int task(int number)
{
std::this_thread::sleep_for(std::chrono::seconds(2));
return 100 + number;
}
void test()
{
// 下面代码执行完成之后,线程函数就开始执行
std::future<int> fut = std::async(std::launch::async, task, 10);
std::cout << "任务开始执行..." << std::endl;
try
{
// 阻塞等待任务函数执行结束,并得到结果
int ret = fut.get();
std::cout << "ret = " << ret << std::endl;
}
catch (const std::exception& ex)
{
std::cout << ex.what() << std::endl;
}
std::cout << "程序结束" << std::endl;
}
int main()
{
test();
return EXIT_SUCCESS;
}
#endif
1.2 非阻塞获得任务结果 {#title-2}
#if 1
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<thread>
#include<chrono>
#include<future>
// 2. 非阻塞方式获得异步任务结果
// wait_for 等待指定长度的时间,返回任务状态:ready、timeout
// wait_until 等待到某个时间点,返回任务状态:ready、timeout
// 例如:等待一段时间后,没有拿到结果,此时可以进行其他的操作。
int calculate(int number)
{
int result = 0;
for (int i = 0; i < number; ++i)
{
std::this_thread::sleep_for(std::chrono::seconds(1));
result += 10;
}
return result;
}
void test()
{
std::future<int> fut = std::async(std::launch::async, calculate, 5);
std::cout << "任务开始执行..." << std::endl;
while (true)
{
std::future_status status = fut.wait_for(std::chrono::seconds(1));
if (status == std::future_status::ready)
{
std::cout << "子线程结果:" << fut.get() << std::endl;
break;
}
if (status == std::future_status::timeout)
{
std::cout << "子任务尚未执行完毕,做会其他的事情..." << std::endl;
}
}
}
int main()
{
test();
return EXIT_SUCCESS;
}
#endif
1.3 没有返回值任务场景 {#title-3}
#if 1
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<thread>
#include<chrono>
#include<future>
#include<fstream>
// wait 阻塞等待任务函数执行结束,并不关心返回值。例如,AB两个线程执行完毕后,将结果写入到文件中,后续由主线程进行文件合并。
void write_file(std::string filename, std::string content)
{
std::ofstream ofs(filename);
// 等待 2 秒,表示耗时任务
std::this_thread::sleep_for(std::chrono::seconds(2));
ofs << content << std::endl;
ofs.close();
}
void test()
{
// 下面代码执行完成之后,线程函数就开始执行
std::future<void> fut1 = std::async(std::launch::async, write_file, "demo1.txt", "hello world");
std::future<void> fut2 = std::async(std::launch::async, write_file, "demo2.txt", "hello python");
std::cout << "任务开始执行..." << std::endl;
// 等待异步任务执行完毕
fut1.wait();
fut2.wait();
// 合并文件
std::ifstream ifs1("demo1.txt");
std::ifstream ifs2("demo2.txt");
std::ofstream ofs("demo.txt");
std::copy(std::istreambuf_iterator<char>(ifs1), std::istreambuf_iterator<char>(), std::ostreambuf_iterator<char>(ofs));
std::copy(std::istreambuf_iterator<char>(ifs2), std::istreambuf_iterator<char>(), std::ostreambuf_iterator<char>(ofs));
ifs1.close();
ifs2.close();
ofs.close();
std::cout << "程序结束" << std::endl;
}
int main()
{
test();
return EXIT_SUCCESS;
}
#endif
- 延迟任务 {#title-4} ==================
2.1 延迟任务的基本使用 {#title-5}
2.2 独享和共享任务结果 {#title-6}
2.3 延迟任务的案例理解 {#title-7}
#if 1
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<thread>
#include<chrono>
#include<future>
#include<map>
// 1. 延迟任务创建和执行
void test01()
{
size_t timeout = 2;
std::future<int> fut = std::async(std::launch::deferred, [&timeout](int number) {
std::cout << "延迟任务开始执行..." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(timeout));
return 100 + number;
}, 10);
// get、wait 调用时,将会触发线程函数执行
int ret = fut.get();
// std::cout << "ret = " << ret << std::endl;
// fut.wait();
// wait_for、wait_until 无法触发线程函数运行,可用来查询任务状态
// deferred 任务尚未开始执行
// timeout 任务正在执行
// ready 任务处理完毕
// fut.wait_for(std::chrono::seconds(2));
// fut.wait_until(std::chrono::steady_clock().now() + std::chrono::seconds(2));
}
// 2. std::shared_future
void test02()
{
std::future<int> fut = std::async(std::launch::deferred, []() {
std::cout << "延迟任务开始执行..." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(2));
return 100;
});
#if 0
// std::future 只能获取一次结果,重复获取结果将会抛出异常
// std::shared_future 可以重复获取结果
int ret = fut.get();
ret = fut.get();
#else
// 解决该问题:可以使用 std::shared_future 代替 std::future
// 可以将 std::future 转换为 std::shared_future,此时 fut 将变得无效
// 注意:不要先对 std::future 调用 get 之后,再进行类型转换,否则得到的 std::shared_future 也是不可用
// 也可以在创建任务时候,直接指定 std::shared_future 类型承接返回值
// 即:std::future<int> fut = std::async(...)
std::shared_future<int> shared_fut = fut.share();
int ret = shared_fut.get();
ret = shared_fut.get();
#endif
}
// 3. 延迟任务的案例
// 假设:用户在网页查看某张图片,但是只有处理过的图片才能够展示给用户
// 我们可以:
// 1. 集中时间处理所有图片,这样的话,会在一段时间时间内占用大量的系统资源,会影响系统性能
// 2. 按需处理图片,用户请求那张就开个线程处理哪张,处理过的图像直接返回,剩余未处理的可以在系统空闲时再集中处理
// 显然,在系统资源有限的情况下,第二种更合适一些。第二种就需要用到延时任务。
std::string data_analysis(std::string path)
{
std::this_thread::sleep_for(std::chrono::seconds(10));
return path;
}
void test03()
{
std::map<std::string, std::shared_future<std::string>> tasks;
// 添加延迟图像处理任务
tasks.insert({ "a.png", std::async(std::launch::deferred, data_analysis, "a.png")});
tasks.insert({ "b.png", std::async(std::launch::deferred, data_analysis, "b.png")});
tasks.insert({ "c.png", std::async(std::launch::deferred, data_analysis, "c.png")});
// 用户查看图像
std::string picture;
while (true)
{
std::cout << "请输入查看的图像:";
std::cin >> picture;
// 查看图像目前状态
std::shared_future<std::string>& fut = tasks[picture];
std::future_status status = fut.wait_for(std::chrono::seconds(1));
if (status == std::future_status::deferred)
{
if (status != std::future_status::timeout)
{
std::cout << "图像尚未处理, 开始处理图像..." << std::endl;
std::thread([&fut]() { fut.wait(); }).detach();
}
}
if (status == std::future_status::timeout)
{
std::cout << "图像正在处理中" << std::endl;
}
if (status == std::future_status::ready)
{
std::cout << "图像处理完毕,您要查看的图像是:" << fut.get() << std::endl;
}
std::cout << "----------------" << std::endl;
}
}
int main()
{
test02();
return EXIT_SUCCESS;
}
#endif