package_tsak 和 promise 的区别
package_tsak
-
作用:
std::packaged_task<>对一个函数或可调用对象,绑定一个期望。当std::packaged_task<> 对象被调用,它就会调用相关函数或可调用对象,将期望状态置为就绪,返回值也会被存储为相关数据
-
个人理解:
是一个可调用对象的包装器:
包装一个可调用对象(个人理解和function一样的效果)异步可获取调用对象的执行结果:
作用和promise 相似
-
应用场景:
这可以用在构建线程池的建筑块(可见第9章),或用于其他任务的管理,比如在任务所在线程上运行任务,或将它们顺序的运行在一个特殊的后台线程上。当一个粒度较大的操作可以被分解为独立的子任务时,其中每个子任务就可以包含在一个std::packaged_task<>实例中,之后这个实例将传递到任务调度器或线程池中。这就是对任务的细节进行抽象了;调度器仅处理std::packaged_task<>实例,要比处理独立的函数高效的多
promise
-
作用:
std::promise提供设定值的方式(类型为T),这个类型会和后面看到的std::future 对象相关联。一对std::promise/std::future会为这种方式提供一个可行的机制;在期望上可以阻塞等待线程,同时,提供数据的线程可以使用组合中的“承诺”来对相关值进行设置,以及将“期望”的状态置为“就绪”。可以通过get_future()成员函数来获取与一个给定的std::promise相关的std::future对象,就像是与std::packaged_task相关。当“承诺”的值已经设置完毕(使用set_value()成员函数),对应“期望”的状态变为“就绪”,并且可用于检索已存储的值。
是一个承诺,它可以在某个时刻存储一个值或一个异常。你可以在另一个地方(比如另一个线程)通过
promise
的set_value
或set_exception
来设置值或异常
关键区别总结
特性 | std::packaged_task | std::promise |
---|---|---|
任务绑定 | ✅ 直接绑定可调用对象 | ❌ 不绑定任务 |
结果设置方式 | 自动(通过任务返回值) | 手动(显式调用set_value) |
执行入口 | 自身调用运算符 () | 需外部执行逻辑 |
典型使用场景 | 任务队列、线程池 | 异步回调、复杂控制流 |
结果设置位置 | 固定(任务结束时) | 任意位置(可跨函数/线程) |
功能 | 注重执行绑定的任务 | 注重设置 数值结果 |
使用场景建议
-
packaged_task
适用场景:- 需要将任务和结果打包传递(如线程池提交任务)
- 简单的一次性异步任务
// 线程池伪代码 thread_pool.post(std::packaged_task{[]{return process_data(); // 自动传递结果 }});
-
promise
适用场景:- 结果产生位置分散(如多个回调函数)
- 需要手动控制结果设置时机
- 与外部API集成(如C风格回调)
void callback(int result, std::promise<int>& prom) {prom.set_value(result); // 在回调中设置结果 }// 注册回调 external_api_register([](int r){callback(r, prom); // 通过回调设置结果 });
-
混合使用场景:
std::promise<void> start_signal; auto fut = start_signal.get_future();std::packaged_task<void()> task([&]{fut.wait(); // 等待启动信号execute_work(); });std::thread t(std::move(task)); // ... start_signal.set_value(); // 手动触发任务执行
核心要点总结
packaged_task = 任务执行 + 结果传递
(二合一容器)promise = 结果传递通道
(需外部驱动任务)- 两者都通过关联的
std::future
获取结果 packaged_task
更适合任务中心化场景,promise
更适合结果分散化场景
理解这个区别后,您可以根据任务结构选择更合适的工具:当任务逻辑集中时用 packaged_task
更简洁;当结果产生路径复杂时用 promise
更灵活。