欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 游戏 > C++学习:六个月从基础到就业——多线程编程:Futures与异步任务

C++学习:六个月从基础到就业——多线程编程:Futures与异步任务

2025/6/20 21:33:11 来源:https://blog.csdn.net/qq_53773901/article/details/148113197  浏览:    关键词:C++学习:六个月从基础到就业——多线程编程:Futures与异步任务

C++学习:六个月从基础到就业——多线程编程:Futures与异步任务

本文是我C++学习之旅系列的第五十七篇技术文章,也是第四阶段"并发与高级主题"的第四篇,介绍C++11标准中提供的异步编程工具:futures、promises和异步任务机制。查看完整系列目录了解更多内容。

引言

在前几篇文章中,我们学习了线程创建、互斥量、条件变量等基础多线程工具。这些工具非常强大,但对于某些任务来说可能过于底层,需要编写大量样板代码。例如,当我们仅需要异步执行一个函数并获取其返回值时,手动创建线程、设置共享状态和同步机制显得过于繁琐。

C++11引入了futures库,提供了更高层次的抽象,使异步编程变得更加简单和优雅。本篇我们将深入探讨std::futurestd::promisestd::async等异步编程工具,了解它们如何简化多线程代码,提高程序的可维护性和效率。

目录

  • 多线程编程:Futures与异步任务
    • 引言
    • 目录
    • Futures与Promises基础
      • std::future概念
      • std::promise概念
      • future与promise协作机制
    • std::async函数
      • 启动策略
      • 返回值与异常处理
      • 与线程的比较
    • std::packaged_task
      • 基本用法
      • 与std::function的区别
      • 在线程池中使用
    • std::shared_future
      • 多次获取结果
      • 多线程等待同一结果
    • 异常处理与取消机制
      • 异步任务中的异常
      • 任务取消与超时
    • 实际应用案例
      • 并行计算与结果聚合
      • 异步IO操作
      • 任务依赖关系管理
      • 长时间运算与UI响应性
    • 最佳实践与陷阱
      • 避免数据竞争
      • 处理长时间运行的任务
      • 资源管理注意事项
    • C++20中的改进:std::jthread与停止标记
    • 总结

Futures与Promises基础

std::future概念

std::future 是一个模板类,表示一个值或异常的"未来"结果。它提供了一种等待异步操作完成并获取其结果的机制,而无需直接处理线程同步的复杂性。

std::future 的核心特性:

  • 允许等待异步操作完成(通过wait方法)
  • 可以获取异步操作的结果(通过get方法)
  • 支持检查状态(通过valid方法)
  • 支持超时等待(通过wait_forwait_until方法)
#include <iostream>
#include <future>
#include <chrono>int compute_value() {// 模拟耗时计算std::cout << "Computing value..." << std::endl;std::this_thread::sleep_for(std::chrono::seconds(2));return 42;
}int main() {// 启动异步任务并获取futurestd::future<int> result_future = std::async(compute_value);// 做其他工作std::cout << "Doing other work..." << std::endl;// 获取结果(如果尚未完成,会阻塞等待)int result = result_future.get();std::cout << "The result is: " << result << std::endl;return 0;
}

std::promise概念

std::promise 是与 std::future 配对的类,它表示"承诺"未来会提供一个值或异常。通过 std::promise,一个线程可以设置一个结果,而另一个线程可以通过对应的 std::future 获取这个结果。

std::promise 的核心特性:

  • 可以设置一个值(通过set_value方法)
  • 可以设置一个异常(通过set_exception方法)
  • 可以获取关联的future(通过get_future方法)
#include <iostream>
#include <future>
#include <thread>void produce_value(std::promise<int> promise) {try {// 模拟耗时计算std::cout << "Producer thread: Computing value..." << std::endl;std::this_thread::sleep_for(std::chrono::seconds(2));// 设置promise的值promise.set_value(42);} catch (...) {// 如果发生异常,设置异常promise.set_exception(std::current_exception());}
}int main() {// 创建promise和关联的futurestd::promise<int> promise;std::future<int> future = promise.get_future();// 在另一个线程中启动生产者std::thread producer_thread(produce_value, std::move(promise));// 主线程做其他工作std::cout << "Main thread: Doing other work..." << std::endl;// 获取结果(阻塞等待)int result = future.get();std::cout << "Main thread: The result is: " << result << std::endl;producer_thread.join();return 0;
}

future与promise协作机制

std::futurestd::promise 通过共享状态协作,该状态由实现内部管理。简单来说,它们的工作原理如下:

  1. 创建 std::promise 对象
  2. 从 promise 获取关联的 std::future
  3. 在一个线程中设置 promise 的值或异常
  4. 在另一个线程中通过 future 等待并获取结果

这种机制提供了一种比条件变量和互斥量更高级的线程间通信方式,特别适合单次传值的场景。

#include <iostream>
#include <future>
#include <thread>
#include <stdexcept>void process(std::promise<std::string> promise) {try {// 模拟过程中可能发生的错误if (rand() % 2 == 0) {throw std::runtime_error("Random processing error");}// 正常情况下,设置结果promise.set_value("Processing completed successfully");} catch (...) {// 捕获并传递异常promise.set_exception(std::current_exception());}
}int main() {srand(static_cast<unsigned int>(time(nullptr)));// 创建promise和futurestd::promise<std::string> promise;std::future<std::string> future = promise.get_future();// 启动处理线程std::thread processing_thread(process, std::move(promise));try {// 获取结果(可能抛出异常)std::string result = future.get();std::cout << "Result: " << result << std::endl;} catch (const std::exception& e) {std::cout << "Exception caught: " << e.what() << std::endl;}processing_thread.join();return 0;
}

std::async函数

std::async 是C++11提供的一个函数模板,它可以异步地执行一个函数,并返回一个表示计算结果的 std::future。它是使用futures最简单、最直接的方式。

启动策略

std::async 接受一个可选的启动策略参数,可以是以下之一:

  • std::launch::async:函数在单独的线程中立即异步启动
  • std::launch::deferred:函数执行被推迟到future的waitget被调用时(惰性求值)
  • std::launch::async | std::launch::deferred(默认):实现可以选择策略
#include <iostream>
#include <future>
#include <thread>
#include <chrono>void print_thread_id(const std::string& prefix) {std::cout << prefix << " thread ID: " << std::this_thread::get_id() << std::endl;
}int main() {print_thread_id("Main");// 强制异步执行auto f1 = std::async(std::launch::async, print_thread_id, "Async");f1.wait();// 延迟执行(在get/wait调用时,在当前线程执行)auto f2 = std::async(std::launch::deferred, print_thread_id, "Deferred");f2.wait();  // 在这一点执行函数// 让实现决定(可能异步,可能延迟)auto f3 = std::async(print_thread_id, "Default");f3.wait();return 0;
}

返回值与异常处理

std::async 会捕获被调用函数的返回值,或者如果函数抛出异常,将异常存储起来。调用 future.get() 时,要么返回计算结果,要么重新抛出异常。

#include <iostream>
#include <future>
#include <stdexcept>int divide(int a, int b) {if (b == 0) {throw std::invalid_argument("Division by zero");}return a / b;
}int main() {// 成功情况auto future_success = std::async(divide, 10, 2);try {int result = future_success.get();std::cout << "Result: " << result << std::endl;} catch (const std::exception& e) {std::cout << "Exception: " << e.what() << std::endl;}// 异常情况auto future_error = std::async(divide, 10, 0);try {int result = future_error.get();std::cout << "Result: " << result << std::endl;} catch (const std::exception& e) {std::cout << "Exception: " << e.what() << std::endl;}return 0;
}

与线程的比较

std::async 相比直接使用 std::thread 有几个明显优势:

  1. 自动管理线程:不需要手动调用joindetach
  2. 返回值处理:可以获取异步函数的返回值
  3. 异常传播:可以捕获异步函数抛出的异常
  4. 线程创建决策:可以让系统决定是否真正创建线程
#include <iostream>
#include <future>
#include <thread>
#include <vector>
#include <numeric>// 计算指定范围内元素的和
int sum_range(const std::vector<int>& v, size_t start, size_t end) {return std::accumulate(v.begin() + start, v.begin() + end, 0);
}int main() {std::vector<int> v(1000);// 填充向量for (size_t i = 0; i < v.size(); ++i) {v[i] = i + 1;  // 1 to 1000}// 使用std::thread的方式int result1 = 0, result2 = 0;std::thread t1([&]() {result1 = sum_range(v, 0, v.size() / 2);});std::thread t2([&]() {result2 = sum_range(v, v.size() / 2, v.size());});t1.join();t2.join();std::cout << "Thread sum: " << (result1 + result2) << std::endl;// 使用std::async的方式auto future1 = std::async(sum_range, std::ref(v), 0, v.size() / 2);auto future2 = std::async(sum_range, std::ref(v), v.size() / 2, v.size());std::cout << "Async sum: " << (future1.get() + future2.get()) << std::endl;return 0;
}

std::packaged_task

std::packaged_task 是一个包装器,它包装了一个可调用对象,并允许异步获取其结果。它可以被看作是 std::functionstd::promise 的组合。

基本用法

使用 std::packaged_task 的基本步骤:

  1. 创建 packaged_task,包装一个函数或可调用对象
  2. 获取关联的 future
  3. 根据需要将任务传递给另一个线程,或在当前线程执行
  4. 通过 future 获取结果
#include <iostream>
#include <future>
#include <thread>int calculate(int x, int y) {return x + y;
}int main() {// 创建packaged_taskstd::packaged_task<int(int, int)> task(calculate);// 获取futurestd::future<int> future = task.get_future();// 在另一个线程中执行任务std::thread task_thread(std::move(task), 2, 3);// 获取结果int result = future.get();std::cout << "Result: " << result << std::endl;task_thread.join();return 0;
}

与std::function的区别

虽然 std::packaged_taskstd::function 都可以包装可调用对象,但它们有重要区别:

  • std::function 仅包装可调用对象,调用时直接返回结果
  • std::packaged_task 不仅包装可调用对象,还可以获取关联的 future
#include <iostream>
#include <functional>
#include <future>int add(int a, int b) {return a + b;
}int main() {// std::function示例std::function<int(int, int)> f = add;int result1 = f(2, 3);  // 直接获取结果std::cout << "function result: " << result1 << std::endl;// std::packaged_task示例std::packaged_task<int(int, int)> task(add);std::future<int> future = task.get_future();task(2, 3);  // 执行任务int result2 = future.get();  // 通过future获取结果std::cout << "packaged_task result: " << result2 << std::endl;return 0;
}

在线程池中使用

std::packaged_task 在线程池实现中特别有用,可以方便地将任务提交到池中,并获取结果:

#include <iostream>
#include <future>
#include <thread>
#include <vector>
#include <queue>
#include <functional>
#include <mutex>
#include <condition_variable>class ThreadPool {
private:std::vector<std::thread> workers;std::queue<std::function<void()>> tasks;std::mutex queue_mutex;std::condition_variable condition;bool stop;public:ThreadPool(size_t threads) : stop(false) {for (size_t i = 0; i < threads; ++i) {workers.emplace_back([this] {while (true) {std::function<void()> task;{std::unique_lock<std::mutex> lock(queue_mutex);condition.wait(lock, [this] { return stop || !tasks.empty(); });if (stop && tasks.empty()) return;task = std::move(tasks.front());tasks.pop();}task();}});}}template<class F, class... Args>auto enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type> {using return_type = typename std::result_of<F(Args...)>::type;// 创建packaged_taskauto task = std::make_shared<std::packaged_task<return_type()>>(std::bind(std::forward<F>(f), std::forward<Args>(args)...));std::future<return_type> res = task->get_future();{std::unique_lock<std::mutex> lock(queue_mutex);if (stop) throw std::runtime_error("enqueue on stopped ThreadPool");// 将packaged_task包装为void函数添加到队列tasks.emplace([task]() { (*task)(); });}condition.notify_one();return res;}~ThreadPool() {{std::unique_lock<std::mutex> lock(queue_mutex);stop = true;}condition.notify_all();for (std::thread &worker : workers) {worker.join();}}
};// 使用示例
int main() {ThreadPool pool(4);  // 4个工作线程的线程池// 提交任务并获取futureauto result1 = pool.enqueue([](int ms) {std::this_thread::sleep_for(std::chrono::milliseconds(ms));return "Task completed after " + std::to_string(ms) + "ms";}, 1000);auto result2 = pool.enqueue([](int a, int b) {return a * b;}, 10, 20);// 获取结果std::cout << result1.get() << std::endl;std::cout << "Product: " << result2.get() << std::endl;return 0;
}

std::shared_future

当多个线程需要访问同一个异步结果时,std::future 就不够用了,因为它的 get 方法只能被调用一次。为此,C++11提供了 std::shared_future,它允许多个线程等待同一个异步结果。

多次获取结果

std::shared_future 可以多次调用 get 方法,每次都会返回结果的副本:

#include <iostream>
#include <future>int main() {// 创建一个futurestd::promise<int> promise;std::future<int> future = promise.get_future();// 将future转换为shared_futurestd::shared_future<int> shared_future = future.share();// 设置promise的值promise.set_value(42);// 多次获取结果std::cout << "Result: " << shared_future.get() << std::endl;std::cout << "Result again: " << shared_future.get() << std::endl;return 0;
}

多线程等待同一结果

std::shared_future 特别适合多个线程等待同一事件的场景,例如程序初始化完成:

#include <iostream>
#include <future>
#include <thread>
#include <vector>void worker(std::shared_future<void> startup_future, int id) {// 等待启动信号startup_future.get();  // 多个线程可以同时等待// 开始工作std::cout << "Worker " << id << " starting..." << std::endl;std::this_thread::sleep_for(std::chrono::milliseconds(id * 100));std::cout << "Worker " << id << " completed" << std::endl;
}int main() {// 创建promise和shared_futurestd::promise<void> startup_promise;std::shared_future<void> startup_future = startup_promise.get_future();// 创建工作线程std::vector<std::thread> threads;for (int i = 1; i <= 5; ++i) {threads.emplace_back(worker, startup_future, i);}// 准备工作std::cout << "Preparing resources..." << std::endl;std::this_thread::sleep_for(std::chrono::seconds(2));// 发送启动信号std::cout << "Resources ready, sending startup signal..." << std::endl;startup_promise.set_value();// 等待所有工作线程完成for (auto& thread : threads) {thread.join();}std::cout << "All workers completed" << std::endl;return 0;
}

异常处理与取消机制

异步任务中的异常

在异步任务中抛出的异常会被捕获并存储,当调用 future.get() 时会重新抛出:

#include <iostream>
#include <future>
#include <stdexcept>void task_that_throws() {throw std::runtime_error("Task failed");
}int main() {// 通过async启动抛出异常的任务std::future<void> future = std::async(std::launch::async, task_that_throws);try {future.get();} catch (const std::exception& e) {std::cout << "Caught exception: " << e.what() << std::endl;}return 0;
}

异常传递机制也适用于 std::promisestd::packaged_task

#include <iostream>
#include <future>
#include <exception>int main() {// 通过promise传递异常std::promise<int> promise;std::future<int> future = promise.get_future();// 设置异常try {throw std::runtime_error("Promise exception");} catch (...) {promise.set_exception(std::current_exception());}// 获取结果(将重新抛出异常)try {int result = future.get();std::cout << "Result: " << result << std::endl;} catch (const std::exception& e) {std::cout << "Caught exception: " << e.what() << std::endl;}return 0;
}

任务取消与超时

C++11的futures不直接支持取消正在执行的任务,但可以使用超时等待来实现有限的取消机制:

#include <iostream>
#include <future>
#include <chrono>bool lengthy_computation(std::atomic<bool>& cancel_flag) {for (int i = 0; i < 10; ++i) {// 检查取消标志if (cancel_flag) {std::cout << "Computation cancelled" << std::endl;return false;}// 模拟工作std::this_thread::sleep_for(std::chrono::milliseconds(500));std::cout << "Step " << i + 1 << " completed" << std::endl;}return true;
}int main() {std::atomic<bool> cancel_flag(false);// 启动任务auto future = std::async(std::launch::async, lengthy_computation, std::ref(cancel_flag));// 等待任务但设置超时if (future.wait_for(std::chrono::seconds(3)) == std::future_status::timeout) {std::cout << "Operation taking too long, requesting cancellation..." << std::endl;cancel_flag = true;  // 请求取消}// 获取最终结果bool success = future.get();if (success) {std::cout << "Computation completed successfully" << std::endl;} else {std::cout << "Computation did not complete successfully" << std::endl;}return 0;
}

C++20引入了 std::jthread 和停止令牌,提供了更好的任务取消支持,我们将在本文后面讨论。

实际应用案例

并行计算与结果聚合

使用futures可以轻松实现并行计算并聚合结果:

#include <iostream>
#include <vector>
#include <numeric>
#include <future>// 计算部分向量的和
long long sum_range(const std::vector<int>& data, size_t start, size_t end) {return std::accumulate(data.begin() + start, data.begin() + end, 0LL);
}int main() {// 创建一个大向量std::vector<int> data(100'000'000);for (size_t i = 0; i < data.size(); ++i) {data[i] = i % 10;}// 确定线程数量和每线程的工作量size_t num_threads = std::thread::hardware_concurrency();size_t block_size = data.size() / num_threads;// 创建和启动任务std::vector<std::future<long long>> futures;for (size_t i = 0; i < num_threads; ++i) {size_t start = i * block_size;size_t end = (i == num_threads - 1) ? data.size() : (i + 1) * block_size;futures.push_back(std::async(std::launch::async, sum_range, std::ref(data), start, end));}// 收集并汇总结果long long total = 0;for (auto& future : futures) {total += future.get();}std::cout << "Sum of vector: " << total << std::endl;return 0;
}

异步IO操作

Futures非常适合处理I/O操作,例如文件读取或网络请求:

#include <iostream>
#include <fstream>
#include <future>
#include <string>
#include <vector>// 异步读取文件内容
std::string read_file(const std::string& filename) {std::ifstream file(filename);if (!file) {throw std::runtime_error("Cannot open file: " + filename);}std::string content;std::string line;while (std::getline(file, line)) {content += line + "\n";}return content;
}int main() {std::vector<std::string> filenames = {"file1.txt", "file2.txt", "file3.txt"};// 启动异步文件读取任务std::vector<std::future<std::string>> file_contents;for (const auto& filename : filenames) {file_contents.push_back(std::async(std::launch::async, read_file, filename));}// 处理每个文件的内容for (size_t i = 0; i < filenames.size(); ++i) {try {std::string content = file_contents[i].get();std::cout << "File " << filenames[i] << " has " << content.size() << " bytes" << std::endl;// 处理文件内容...} catch (const std::exception& e) {std::cerr << "Error reading " << filenames[i] << ": " << e.what() << std::endl;}}return 0;
}

任务依赖关系管理

使用futures可以有效管理具有依赖关系的任务:

#include <iostream>
#include <future>
#include <vector>
#include <string>// 模拟任务依赖
std::string stage1() {std::this_thread::sleep_for(std::chrono::seconds(1));return "Stage 1 result";
}std::string stage2(std::string input) {std::this_thread::sleep_for(std::chrono::seconds(1));return input + " -> Stage 2 result";
}std::string stage3(std::string input) {std::this_thread::sleep_for(std::chrono::seconds(1));return input + " -> Stage 3 result";
}int main() {// 启动第一阶段auto future1 = std::async(std::launch::async, stage1);// 可以做其他工作...std::cout << "Setting up pipeline..." << std::endl;// 使用第一阶段的结果启动第二阶段auto future2 = std::async(std::launch::async, [&future1]() {return stage2(future1.get());  // 等待stage1完成});// 使用第二阶段的结果启动第三阶段auto future3 = std::async(std::launch::async, [&future2]() {return stage3(future2.get());  // 等待stage2完成});// 获取最终结果std::string final_result = future3.get();std::cout << "Final result: " << final_result << std::endl;return 0;
}

长时间运算与UI响应性

在具有用户界面的应用程序中,使用futures可以保持UI响应性:

#include <iostream>
#include <future>
#include <chrono>
#include <thread>// 模拟UI更新
void update_progress(int percentage) {std::cout << "\rProgress: [";for (int i = 0; i < 20; ++i) {if (i < percentage / 5) {std::cout << "=";} else {std::cout << " ";}}std::cout << "] " << percentage << "%" << std::flush;
}// 模拟耗时计算
int complex_calculation() {int result = 0;for (int i = 0; i <= 100; ++i) {// 模拟工作std::this_thread::sleep_for(std::chrono::milliseconds(50));result += i;}return result;
}int main() {// 启动后台计算auto calculation_future = std::async(std::launch::async, complex_calculation);// 主线程显示进度bool done = false;int progress = 0;while (!done) {// 检查计算是否完成auto status = calculation_future.wait_for(std::chrono::milliseconds(100));if (status == std::future_status::ready) {done = true;progress = 100;} else {// 模拟进度估算progress = std::min(progress + 2, 99);}// 更新UIupdate_progress(progress);// 处理用户输入(简化为休眠)std::this_thread::sleep_for(std::chrono::milliseconds(100));}// 获取计算结果int result = calculation_future.get();std::cout << "\nCalculation complete! Result: " << result << std::endl;return 0;
}

最佳实践与陷阱

避免数据竞争

使用futures并不能完全消除数据竞争。当多个任务共享数据时,仍需要适当的同步:

#include <iostream>
#include <future>
#include <vector>
#include <mutex>void potentially_unsafe(std::vector<int>& data, size_t start, size_t end) {for (size_t i = start; i < end; ++i) {data[i] *= 2;  // 如果多个线程重叠操作范围,会发生数据竞争}
}void safe_with_mutex(std::vector<int>& data, std::mutex& mtx, size_t start, size_t end) {std::lock_guard<std::mutex> lock(mtx);for (size_t i = start; i < end; ++i) {data[i] *= 2;}
}void safe_partitioned(std::vector<int>& data, size_t start, size_t end) {// 任务操作独立的数据区域,无需互斥量for (size_t i = start; i < end; ++i) {data[i] *= 2;}
}int main() {std::vector<int> data(1000, 1);std::mutex mtx;// 不安全的方法:两个任务可能重叠访问auto f1 = std::async(std::launch::async, potentially_unsafe, std::ref(data), 0, 600);auto f2 = std::async(std::launch::async, potentially_unsafe, std::ref(data), 400, 1000);f1.wait();f2.wait();// 用互斥量保证安全data.assign(1000, 1);auto f3 = std::async(std::launch::async, safe_with_mutex, std::ref(data), std::ref(mtx), 0, 600);auto f4 = std::async(std::launch::async, safe_with_mutex, std::ref(data), std::ref(mtx), 400, 1000);f3.wait();f4.wait();// 更高效的方法:任务操作不同的数据区域data.assign(1000, 1);auto f5 = std::async(std::launch::async, safe_partitioned, std::ref(data), 0, 500);auto f6 = std::async(std::launch::async, safe_partitioned, std::ref(data), 500, 1000);f5.wait();f6.wait();return 0;
}

处理长时间运行的任务

对于长时间运行的任务,需要考虑超时和取消机制:

#include <iostream>
#include <future>
#include <chrono>
#include <atomic>int long_task(std::atomic<bool>& cancel_flag) {int result = 0;for (int i = 0; i < 10; ++i) {if (cancel_flag.load()) {std::cout << "Task cancelled at step " << i << std::endl;return -1;  // 表示取消}// 模拟耗时工作std::this_thread::sleep_for(std::chrono::seconds(1));result += i;}return result;
}int main() {std::atomic<bool> cancel_flag(false);// 启动长任务auto future = std::async(std::launch::async, long_task, std::ref(cancel_flag));// 等待结果,但设置超时for (int i = 0; i < 15; ++i) {auto status = future.wait_for(std::chrono::seconds(1));if (status == std::future_status::ready) {std::cout << "Task completed with result: " << future.get() << std::endl;break;}std::cout << "Waiting... (" << (i+1) << " seconds)" << std::endl;// 如果等待太长时间,取消任务if (i >= 7) {  // 等待8秒后取消std::cout << "Timeout reached, requesting cancellation" << std::endl;cancel_flag.store(true);}}// 确保获取结果(即使已取消)if (future.valid()) {try {int result = future.get();std::cout << "Final result: " << result << std::endl;} catch (const std::exception& e) {std::cout << "Exception: " << e.what() << std::endl;}}return 0;
}

资源管理注意事项

使用futures时需要注意几个资源管理问题:

  1. std::future析构函数不会等待任务完成,但C++20的std::jthread
  2. 如果丢弃了一个future,其关联的线程可能会变成"孤儿"线程
  3. 某些操作(如析构一个包含有效future的对象)可能会隐式调用wait(),导致阻塞
#include <iostream>
#include <future>
#include <vector>void demonstrate_future_lifetime() {std::cout << "Starting future lifetime example..." << std::endl;// 创建future并立即丢弃{auto future = std::async(std::launch::async, []() {std::this_thread::sleep_for(std::chrono::seconds(2));std::cout << "Task executed!" << std::endl;return 42;});// future在这里被销毁,析构函数会隐式调用wait()// 导致当前线程阻塞,直到异步任务完成}std::cout << "Future destroyed, continuing..." << std::endl;// 更加安全的方式:始终显式处理future{auto future = std::async(std::launch::async, []() {std::this_thread::sleep_for(std::chrono::seconds(2));return 42;});// 选择不阻塞的处理方式if (future.wait_for(std::chrono::seconds(0)) != std::future_status::ready) {std::cout << "Task not ready, detaching..." << std::endl;// 将future转移到保持线程运行的辅助对象// 在实际应用中,可能需要某种任务管理器} else {int result = future.get();std::cout << "Task result: " << result << std::endl;}}
}int main() {demonstrate_future_lifetime();// 给线程足够时间完成输出std::this_thread::sleep_for(std::chrono::seconds(3));return 0;
}

C++20中的改进:std::jthread与停止标记

C++20引入了std::jthread和停止标记,提供了更好的线程生命周期管理和协作式任务取消:

#include <iostream>
#include <thread>
#include <chrono>
#include <stop_token>// 注意:此代码需要C++20支持
void task_with_stop(std::stop_token stop_token) {for (int i = 0; i < 10; ++i) {// 检查是否请求停止if (stop_token.stop_requested()) {std::cout << "Stop requested, exiting..." << std::endl;return;}std::cout << "Working: step " << i << std::endl;std::this_thread::sleep_for(std::chrono::milliseconds(500));}std::cout << "Task completed normally" << std::endl;
}int main() {// 创建一个jthread并传递停止标记给任务std::jthread worker(task_with_stop);// 主线程工作std::this_thread::sleep_for(std::chrono::seconds(2));// 请求线程停止std::cout << "Requesting task to stop" << std::endl;worker.request_stop();// jthread的析构函数会自动调用join(),无需显式join// worker.join();std::cout << "Main thread exiting" << std::endl;return 0;
}

std::jthread的主要优势:

  1. 析构函数自动调用join(),避免"孤儿"线程
  2. 内置停止标记机制,支持协作式任务取消
  3. 与futures结合使用时,提供更好的资源管理

总结

C++11的futures库为异步编程提供了更高层次的抽象,简化了多线程代码,减少了常见的同步错误。通过本文,我们详细了解了:

  • std::futurestd::promise如何协作传递异步结果
  • std::async如何简化异步任务的创建和结果获取
  • std::packaged_task如何包装可调用对象,便于在线程间传递
  • std::shared_future如何支持多个线程等待同一结果
  • 如何在异步任务中处理异常和超时
  • 实际应用中的最佳实践和常见陷阱

C++20通过std::jthread和停止标记进一步改进了异步编程模型,为任务取消和线程生命周期管理提供了更好的支持。

掌握这些异步编程工具对构建高性能、可靠的多线程应用至关重要。在下一篇文章中,我们将探讨并发容器和无锁编程,进一步提升多线程代码的性能和可靠性。


这是我C++学习之旅系列的第五十七篇技术文章。查看完整系列目录了解更多内容。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词