随笔 - 741  文章 - 0  评论 - 260  阅读 - 416万

std::promise 和 std::packaged_task

std::promisestd::packaged_task 都是 C++11 标准库中用于管理异步操作的工具,它们都允许你通过 std::future 获取异步操作的结果。然而,它们在设计目的和使用场景上有显著的区别。以下是对两者的详细比较:

std::promise

主要用途

  • 手动设置结果:std::promise 提供了一种机制来手动设置异步操作的结果或异常,并通过关联的 std::future 对象获取该结果。
  • 灵活控制:适用于需要显式控制何时以及如何设置结果的场景。

常用方法

  • set_value():设置共享状态的结果(适用于返回值类型)。
void set_value(const T& value); // 或者
void set_value(T&& value);

  set_exception():设置共享状态的异常信息。

void set_exception(std::exception_ptr p);

get_future():返回一个与当前 std::promise 关联的 std::future 对象。

std::future<T> get_future();
复制代码
#include <iostream>
#include <future>
#include <thread>

void producer(std::promise<int> prom) {
    try {
        int result = 42; // 计算结果
        prom.set_value(result); // 设置共享状态的值
    } catch (...) {
        prom.set_exception(std::current_exception()); // 设置异常
    }
}

int main() {
    std::promise<int> prom;
    std::future<int> fut = prom.get_future(); // 获取与 promise 关联的 future

    std::thread t(producer, std::move(prom)); // 启动新线程执行生产者任务

    try {
        int value = fut.get(); // 阻塞直到共享状态就绪
        std::cout << "Promise returned: " << value << "\n";
    } catch (const std::exception& e) {
        std::cerr << "Exception caught: " << e.what() << "\n";
    }

    t.join(); // 等待线程结束
    return 0;
}
复制代码

 

std::packaged_task

主要用途

  • 包装可调用对象:std::packaged_task 将任何可调用对象(如函数、lambda 表达式或函数对象)包装成一个异步任务,并返回一个 std::future 对象来获取该任务的结果。
  • 自动设置结果:任务执行完成后,std::packaged_task 自动设置结果或异常到关联的 std::future 对象。

常用方法

构造函数:接受一个可调用对象并包装它。

template< class Function >
explicit packaged_task( Function&& f );

operator():调用被包装的任务。

void operator()( Args... args );

get_future():返回一个与当前 std::packaged_task 关联的 std::future 对象。

get_future():返回一个与当前 std::packaged_task 关联的 std::future 对象。

 

复制代码
#include <iostream>
#include <future>
#include <thread>

int task_function(int x) {
    return x * x;
}

int main() {
    std::packaged_task<int(int)> task(task_function); // 包装任务
    std::future<int> fut = task.get_future(); // 获取与 packaged_task 关联的 future

    std::thread t(std::move(task), 5); // 启动新线程执行任务

    int value = fut.get(); // 阻塞直到任务完成
    std::cout << "Packaged task returned: " << value << "\n";

    t.join(); // 等待线程结束
    return 0;
}
复制代码

 

特性std::promisestd::packaged_task
主要用途 手动设置异步操作的结果或异常 包装可调用对象为异步任务,并自动设置结果或异常
灵活性 更灵活,适合需要显式控制何时及如何设置结果的场景 较少灵活性,但更方便直接执行可调用对象
设置结果的方式 使用 set_value() 和 set_exception() 自动设置结果或异常
获取 std::future 通过 get_future() 通过 get_future()
适用场景 当你需要在多个地方设置结果或处理复杂的异步逻辑时 当你需要简单地将一个函数或可调用对象包装为异步任务时

具体应用场景

  • std::promise

    • 当你需要在多个不同的地方设置异步操作的结果或异常时。
    • 当你需要手动控制何时以及如何设置结果时。
    • 在复杂的多线程环境中,可能需要在不同线程之间传递数据和状态时。
  • std::packaged_task

    • 当你有一个具体的可调用对象(如函数或 lambda 表达式),并且希望将其作为异步任务执行时。
    • 当你需要简化异步任务的创建和管理时,特别是当任务的结果可以直接从可调用对象中获得时。

      综合示例

      为了更好地理解两者的区别,下面是一个综合示例,展示了如何分别使用 std::promisestd::packaged_task 来实现相同的功能:

      复制代码
      #include <iostream>
      #include <future>
      #include <thread>
      
      void async_operation(std::promise<int> prom) {
          try {
              int result = 42; // 模拟异步操作
              prom.set_value(result);
          } catch (...) {
              prom.set_exception(std::current_exception());
          }
      }
      
      int main() {
          std::promise<int> prom;
          std::future<int> fut = prom.get_future();
      
          std::thread t(async_operation, std::move(prom));
      
          int value = fut.get();
          std::cout << "Promise returned: " << value << "\n";
      
          t.join();
          return 0;
      }
      复制代码

      使用 std::packaged_task

      复制代码
      #include <iostream>
      #include <future>
      #include <thread>
      
      int async_operation(int x) {
          return x * x; // 模拟异步操作
      }
      
      int main() {
          std::packaged_task<int(int)> task(async_operation);
          std::future<int> fut = task.get_future();
      
          std::thread t(std::move(task), 5);
      
          int value = fut.get();
          std::cout << "Packaged task returned: " << value << "\n";
      
          t.join();
          return 0;
      }
      复制代码

       

      总结

      • std::promise 提供了更高的灵活性,适合需要手动控制何时及如何设置异步操作结果的场景。
      • std::packaged_task 更加便捷,适合将一个具体的可调用对象包装为异步任务,并自动管理其结果或异常。

      根据具体的需求选择合适的工具可以提高代码的可读性和维护性。



posted on   莫水千流  阅读(28)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
历史上的今天:
2015-01-14 Levenshtein Distance (编辑距离) 算法详解
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示