std::async 是 C++11 引入的一个工具函数,它主要用于在程序中创建 异步任务、延迟任务。本篇文章将通过设计的 6 个示例程序来展示如何利用 std::async 实现异步任务与延迟任务。
1. 异步任务
一个任务或操作可以独立于主线程(或主任务)运行,主线程无需等待任务完成便可继续执行其他工作。当异步任务完成时,可以通过某种机制(如 std::future)获取其结果。
1.1 异步任务的基本使用
这一小节主要介绍如何创建异步任务,以及如何获得异步任务的返回结果。这里需要注意的是:std::async 函数的第一个参数如果没有指定的话,那么默认任务是异步任务还是延迟任务取决于具体实现。
#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 没有返回值任务场景
wait 函数会阻塞等待任务函数执行结束(正常结束、异常结束),并不关心返回值。注意:如果任务函数抛出异常,wait 函数不会再抛出,而是保存到 std::future 对象内部,当调用 get 函数时会抛出异常。
案例:A B 两个异步任务分别执行将不同的内容写入到不同的文件中,然后由主线程将两个文件的内容合并到第三个文件中。
#if 1
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<thread>
#include<chrono>
#include<future>
#include<fstream>
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
1.3 非阻塞获得任务结果
get 和 wait 都是阻塞等待任务结束。也可以通过 wait_for、wait_until 来非阻塞方式等待异步任务结果。
- wait_for 会在指定时间内等待任务状态变化,如果任务没有结束,返回 timeout,否则返回 ready
- wait_until 则表示等待到某个时间点,如果任务状态没有发生变化,则返回 timeout,否则返回 ready
我们可以通过不停的检查任务的状态,来实现非阻塞的方式获得任务结果。
#if 1
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<thread>
#include<chrono>
#include<future>
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));
// std::future_status status = fut.wait_until(std::chrono::steady_clock().now() + std::chrono::seconds(2));
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
2. 延迟任务
将任务的执行推迟,直到确实需要时才开始执行。这种方式通常用于系统负载较高时,任务的结果又不立即需要的场景,帮助系统优化资源使用,提升整体性能。
2.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(8));
return 100 + number;
}
void test01()
{
// 任务不会马上执行
std::future<int> fut = std::async(std::launch::deferred, task, 10);
// 当调用 get、wait 函数时,触发任务函数执行
int ret = fut.get();
// std::cout << "ret = " << ret << std::endl;
// fut.wait();
}
void test02()
{
std::future<int> fut = std::async(std::launch::deferred, task, 10);
bool is_start = false;
while (true)
{
// 当延迟任务尚未执行前,wait_for 函数不会阻塞,会立即返回
std::future_status status = fut.wait_for(std::chrono::seconds(1));
if (status == std::future_status::deferred && !is_start)
{
std::cout << "任务尚未开始执行..." << std::endl;
std::thread([&fut]() { fut.wait(); }).detach();
is_start = true;
}
if (status == std::future_status::timeout)
{
std::cout << "任务正在执行..." << std::endl;
}
if (status == std::future_status::ready)
{
std::cout << "任务执行完毕,执行结果是:" << fut.get() << std::endl;
break;
}
}
}
int main()
{
test02();
return EXIT_SUCCESS;
}
#endif
2.2 独享和共享任务结果
我们一直使用 std::future 来获得任务的结果,但是它适合一次性获取结果,多次获取会导致异常。当任务需要在多个地方、或者多次获取结果时,需要使用 std::shared_future 来代替 std::future。
- 我们可以在调用
std::async函数时,直接使用std::shared_future来承接返回值。 - 也可以调用
std::future的share函数来返回一个std::shared_future类型的对象。
#if 1
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<thread>
#include<chrono>
#include<future>
void test()
{
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<int> shared_fut = fut.share();
int ret = shared_fut.get();
ret = shared_fut.get();
#endif
}
int main()
{
test();
return EXIT_SUCCESS;
}
#endif
2.3 延迟任务的案例理解
假设:用户在网页查看某张图片,但是只有处理过的图片才能够展示给用户。我们可以:
- 集中时间处理所有图片,这样的话,会在一段时间时间内占用大量的系统资源,会影响系统性能
- 按需处理图片,用户请求那张就开个线程处理哪张,处理过的图像直接返回,剩余未处理的可以在系统空闲时再集中处理
显然,在系统资源有限的情况下,第二种更合适一些。第二种就需要用到延时任务。
#if 1
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<thread>
#include<chrono>
#include<future>
#include<map>
std::string data_analysis(std::string path)
{
std::this_thread::sleep_for(std::chrono::seconds(10));
return path;
}
void test()
{
std::map<std::string, std::tuple<bool, std::shared_future<std::string>>> tasks;
// 添加延迟图像处理任务
tasks.insert({ "a.png", {false, std::async(std::launch::deferred, data_analysis, "a.png")} });
tasks.insert({ "b.png", {false, std::async(std::launch::deferred, data_analysis, "b.png")} });
tasks.insert({ "c.png", {false, std::async(std::launch::deferred, data_analysis, "c.png")} });
// 用户查看图像
std::string name;
while (true)
{
std::cout << "请输入查看的图像:";
std::cin >> name;
if (name == "exit")
{
for (auto& task : tasks)
{
std::cout << task.first << " " << std::boolalpha << std::get<0>(task.second) << std::endl;
}
std::cout << "----------------" << std::endl;
break;
}
// 查看图像目前状态
auto& picture = tasks[name];
auto& is_ok = std::get<0>(picture);
auto& fut = std::get<1>(picture);
if (is_ok)
{
std::cout << "您要查看的图像是:" << fut.get() << std::endl;
continue;
}
std::future_status status = fut.wait_for(std::chrono::seconds(1));
if (status == std::future_status::deferred)
{
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)
{
is_ok = true;
}
std::cout << "----------------" << std::endl;
}
}
int main()
{
test();
return EXIT_SUCCESS;
}
#endif



冀公网安备13050302001966号
支持一下。