掌握C++异步编程: std::async, std::future 和 std::promise

引言

在现代软件开发中, 异步编程是一种常用技术, 特别是在处理耗时操作时, 例如网络请求, 文件读写或计算密集型任务. 同步执行这些操作会阻塞主线程, 降低性能和用户体验. 为了解决这些问题, C++ 提供了一套强大的异步工具, 包括 std::async, std::futurestd::promise, 简化了并发编程.

本文将通过详细的实例, 讲解这些工具的核心概念, 用法以及它们之间的关系.


什么是异步编程?

异步编程是指任务可以独立于主线程运行, 其结果在完成后异步返回, 而主线程可以继续执行其他任务. 这种方式通常采用"生产者-消费者模型":

在 C++ 中, std::async 用于启动异步任务, std::future 用于获取结果, std::promise 则是负责设置结果的工具.


示例代码: 同步 vs 异步

以下代码展示了同步任务和异步任务的对比:

#include <future>
#include <iostream>
#include <thread>

int main() {
  // 同步任务
  int res = 0;
  std::thread t([&res] { res = 42; });
  t.join();
  std::cout << "同步结果: res = " << res << std::endl;

  // 异步任务
  std::future<int> f = std::async([] { return 42; });
  std::cout << "异步结果: f.get() = " << f.get() << std::endl;

  return 0;
}

输出结果:

同步结果: res = 42
异步结果: f.get() = 42

std::async: 启动异步任务

基本用法

std::async 是一个模板函数, 用于启动异步任务. 其定义如下:

template< class F, class... Args >
std::future<...> async( F&& f, Args&&... args );

以下是一个简单示例:

#include <future>
#include <iostream>

int compute(int a, int b) { return a + b; }

int main() {
  // 使用函数启动异步任务
  std::future<int> result = std::async(compute, 2, 3);

  // 使用 lambda 表达式启动异步任务
  std::future<int> lambdaResult = std::async([]() { return 42; });

  std::cout << "Result: " << result.get() << std::endl;
  std::cout << "Lambda Result: " << lambdaResult.get() << std::endl;

  return 0;
}

启动策略

// 异步启动
std::future<int> result1 = std::async(std::launch::async, compute, 2, 3);
// 延迟执行
std::future<int> result2 = std::async(std::launch::deferred, compute, 2, 3);

std::future: 异步结果容器

前面我们看到std::async的返回值是一个std::future, 那什么是std::future呢?

std::future 是异步任务的结果容器, 用于存储异步计算完成后的结果. 它与 std::promise, std::asyncstd::packaged_task 配合使用.

同步结果访问:

使用方法 get() 来阻塞调用线程, 直到异步任务完成并返回结果.

非阻塞检查:

异步任务的状态检查

使用 std::future_status 检查任务状态,

单次访问:

std::future 的结果只能访问一次, 调用 get() 后将变为无效.

示例代码

#include <iostream>
#include <future>
#include <chrono>

int slowTask() {
    std::this_thread::sleep_for(std::chrono::seconds(2));
    return 42;
}

int main() {
    std::future<int> futureObj = std::async(std::launch::async, slowTask);

    if (futureObj.wait_for(std::chrono::seconds(1)) == std::future_status::timeout) {
        std::cout << "Task is taking too long..." << std::endl;
    }

    int result = futureObj.get(); // 等待任务完成并获取结果
    std::cout << "Result: " << result << std::endl;

    return 0;
}

std::promise: 设置异步结果

std::promise 是与 std::future 配合使用的工具, 用于设置异步任务的结果. 可以理解为 std::promise 是负责"写", 而 std::future 是负责"读".

基本用法

#include <future>
#include <iostream>
#include <thread>

void calculate(std::promise<int>& promise) {
  int result = 42;
  promise.set_value(result);
}

int main() {
  // 1. 创建 promise 和 future
  std::promise<int> promise;
  std::future<int> future = promise.get_future();

  // 2. 异步执行任务
  std::thread t(calculate, std::ref(promise));

  // 3. 获取结果
  int result = future.get();
  std::cout << "result: " << result << std::endl;

  t.join();
  return 0;
}

传递异常

std::promise 还可以用来将异常传递给 std::future.

#include <future>
#include <iostream>
#include <stdexcept>
#include <thread>

void calculate(std::promise<int>& promise) {
  try {
    throw std::runtime_error("An error occurred!");
  } catch (...) {
    // 设置异常
    promise.set_exception(std::current_exception());
  }
}

int main() {
  std::promise<int> promise;
  std::future<int> futureObj = promise.get_future();

  std::thread t(calculate, std::ref(promise));

  try {
    // 这里会捕获异常
    int result = futureObj.get();
  } catch (const std::exception& e) {
    std::cout << "Caught exception: " << e.what() << std::endl;
  }

  t.join();
  return 0;
}

总结

C++ 提供了强大的异步编程工具:

这些工具广泛应用于后台任务处理, 高性能计算和多线程环境中, 是现代 C++ 并发编程的重要基础.

源码链接

源码链接