在 C++ 并发编程中,future、promise、packaged_task 和 async 各自扮演什么角色?请从功能、使用场景和相互关联的角度分析它们的区别。
考察说明
考察对 C++ 并发原语的理解和区分能力,以及在实际场景中的选择依据。
回答思路
- 【回答框架 1】future 是异步操作结果的占位符,它提供 get 和 wait 等接口,用于获取或等待结果。promise 是结果的提供者,通过 set_value 或 set_exception 来设置结果,并与 future 通过共享状态关联,实现线程间的数据传递。
- 【回答框架 2】packaged_task 将可调用对象包装,并产生 future 来获取其返回值,它更灵活,可以在任意线程显式启动,也可作为任务队列的一部分。async 是高级封装,自动管理线程的创建与调度,根据参数(如 launch::async 或 launch::deferred)决定立即异步执行还是延迟到 get 时同步执行。
- 【回答框架 3】区别在于抽象层次和目的:promise 和 packaged_task 更底层,允许手动控制任务的执行时机和结果传递;packaged_task 侧重任务本身,promise 侧重结果传递;future 是结果消费方的接口;async 则提供了最简洁的异步执行方式,但控制粒度较粗,线程调度由库决定。
- 【回答框架 4】在实际使用中,若需要细粒度控制任务执行,选 packaged_task;若需要在线程间手动传递值,选 promise;若只需简单异步执行并获取结果,async 最方便。但要注意 async 可能默认在后台线程执行,需预估资源消耗,而 packaged_task 和 promise 执行需要显式绑定线程,例如配合 std::thread。
- 【关键点 1】future 是异步结果占位符,提供 get 和 wait;promise 通过 set_value/set_exception 与 future 通信。
- 【关键点 2】packaged_task 包装可调用对象并关联 future,可手动控制启动。
- 【关键点 3】async 是对线程创建和任务调度的封装,最简洁,但控制粒度粗。
- 【关键点 4】底层机制都是共享状态,用于线程间数据同步。
- 【易错点 1】get 只能调用一次,多次调用会抛异常。
- 【易错点 2】promise 和 packaged_task 不显式启动线程,需配合 std::thread 或手动调用。
- 【易错点 3】async 可能默认惰性求值,导致阻塞,需注意启动策略。