随笔 - 741  文章 - 0  评论 - 260  阅读 - 416万
  2025年1月14日

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 @ 2025-01-14 20:48 莫水千流 阅读(22) 评论(0) 推荐(0) 编辑
  2025年1月10日
摘要: std::condition_variable 是 C++11 标准库中提供的一个同步原语,用于线程间的条件等待和通知机制。它允许一个或多个线程等待某个条件变为真(即被满足),并通过另一个线程的通知来唤醒这些等待的线程。 主要特点 条件等待:线程可以等待某个条件变量,直到其他线程通知该条件已被满足。 阅读全文
posted @ 2025-01-10 09:33 莫水千流 阅读(303) 评论(0) 推荐(0) 编辑
  2025年1月9日
摘要: std::type_index 本身并不直接支持 std::vector 或其他具体类型的容器,但它可以用来表示和比较任何类型的类型信息,包括 std::vector。你可以使用 std::type_index 来获取和比较 std::vector 的类型信息。 获取 std::vector 的 s 阅读全文
posted @ 2025-01-09 21:16 莫水千流 阅读(11) 评论(0) 推荐(0) 编辑
摘要: std::type_index 是 C++11 引入的一个标准库工具,用于简化和优化对 std::type_info 对象的处理。它提供了一种轻量级的方式来引用类型信息,并支持高效的比较操作。这对于需要在运行时检查或存储类型信息的应用场景非常有用。 主要特性 高效比较:std::type_index 阅读全文
posted @ 2025-01-09 21:01 莫水千流 阅读(41) 评论(0) 推荐(0) 编辑
摘要: 在C++中,保存和遍历类型信息可以通过多种方式实现,具体取决于你想要达到的目标。以下是几种常见的方法: 1. 使用 typeid 和 type_info C++ 提供了 typeid 操作符和 std::type_info 类来获取运行时类型信息(RTTI)。你可以使用这些工具来保存类型信息并在需要 阅读全文
posted @ 2025-01-09 20:56 莫水千流 阅读(33) 评论(0) 推荐(0) 编辑
  2022年11月7日
摘要: // render boxes for (unsigned int i = 0; i < 5; i++) { // calculate the model matrix for each object and pass it to shader before drawing glm::mat4 mo 阅读全文
posted @ 2022-11-07 18:02 莫水千流 阅读(330) 评论(0) 推荐(0) 编辑
  2022年11月4日
摘要: 本文讨论纹理单元(texture unit)的使用,以及和着色器之间的关系。 使用纹理的过程大概是这样的: 1 从硬盘加载texture到内存,获取texture's ID // 1 创建纹理对象获取IDglGenTextures(1, &textureID);// 2 绑定纹理缓冲区设置属性glB 阅读全文
posted @ 2022-11-04 14:10 莫水千流 阅读(1154) 评论(0) 推荐(0) 编辑
  2022年9月26日
摘要: 在Linux系统中,冒号(:)常用来做路径的分隔符(PATH),数据字段的分隔符(/etc/passwd)等。其实,冒号(:)在Bash中也是一个内建命令,它啥也不做,是个空命令、只起到占一个位置的作用,但有时候确实需要它。当然,它也有它的用途的,否则没必要存在。在·Linux的帮助页中说它除了参数 阅读全文
posted @ 2022-09-26 10:37 莫水千流 阅读(1176) 评论(0) 推荐(0) 编辑
  2022年9月13日
摘要: 深入浅出cryptoPP密码学库》学习笔记。crypto++库帮助文档:https://www.cryptopp.com/docs/ref/index.html 进制与编码# 以2进制,8进制,10进制,16进制字符串构造整数 Copy Integer Int2("011111101010000b" 阅读全文
posted @ 2022-09-13 17:10 莫水千流 阅读(125) 评论(0) 推荐(0) 编辑
  2022年9月8日
摘要: 本文为学习OpenGL的学习笔记,如有书写和理解错误还请大佬扶正; 一,纹理缓冲区 一个纹理包含两个主要组成部分,纹理采样状态和包含纹理值得数据缓冲区; 1,为什么使用纹理缓冲区? 纹理缓冲区也称texBO或TBO,允许我们完成一些传统纹理不能完成的工作,首先,纹理缓冲区能够直接填充来自其他渲染结果 阅读全文
posted @ 2022-09-08 11:45 莫水千流 阅读(629) 评论(0) 推荐(0) 编辑
< 2025年2月 >
26 27 28 29 30 31 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 1
2 3 4 5 6 7 8

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