【CPP】std::future
概述:
std::future
的用法整理
[toc]
介绍
1 |
|
std::future
是C++11标准库(并发支持库)中的一个[模板类],它表示一个异步操作的结果。当我们在多线程编程中使用异步任务时,std::future
可以帮助我们在需要的时候获取任务的执行结果。std::future
的一个重要特性是能够阻塞当前线程,直到异步操作完成,从而确保我们在获取结果时不会遇到未完成的操作。
成员函数
- 构造函数
future() noexcept
: 默认构造函数。构造无共享状态的std::future
。构造后,valid()
== falsefuture( future&& other ) noexcept
:移动构造函数。用移动语义,以 other 的共享状态构造 std::future 。构造后 other.valid()
== false 。future( const future& other ) = delete
: 不可复制构造 (CopyConstructible) 。
- 析构函数
~future()
; - operator=
future& operator=( future&& other ) noexcept
:释放任何共享状态并移动赋值 other 的内容给 *this 。赋值后, other.valid() == false 且 this->valid() 将产生与 other.valid() 在赋值前相同的值。future& operator=( const future& other ) = delete
: 不可复制赋值 (CopyAssignable) 。
share() noexcept
: 转移 *this 的共享状态到std::shared_future
对象。多个std::shared_future
对象可引用同一共享对象,调用后valid()
== false 。T get()
: get 方法等待直至 future 拥有合法结果并(依赖于使用哪个模板)获取它。它等效地调用wait()
等待结果。泛型模板和二个模板特化各含单个 get 版本。 get 的三个版本仅在返回类型有别。若调用此函数前valid()
为 false 则行为未定义。释放任何共享状态。调用此方法后valid()
为 false 。bool valid()
: 检查future是否可以共享状态。wait()
: 等待结果变得可用- wait_for
- wait_until
作用
- 异步操作的结果获取:
std::future
提供了一种机制,允许我们在多线程环境中安全地获取异步操作的结果。 - 隐藏异步操作的细节:
std::future
将异步操作的结果封装起来,使程序员无需关注线程同步和通信的具体实现细节。 - 线程同步:通过阻塞等待异步操作完成,
std::future
可以确保我们在继续执行其他操作之前,已经获取了所需的结果。 - 异常处理:
std::future
可以捕获异步操作中抛出的异常,并在获取结果时重新抛出,也可以在主线程中对其进行处理,从而使得异常处理更加简单。 - 提高性能:
std::future
使得我们能够更好地利用多核处理器的性能,通过并行执行任务来提高程序的执行效率。
使用场景
异步任务
当我们需要在后台执行一些耗时操作时,如[文件读写]、网络请求或计算密集型任务,std::future
可以用来表示这些异步任务的结果。通过将任务与主线程分离,我们可以实现任务的并行处理,从而提高程序的执行效率。
并发控制
在[多线程编程]中,我们可能需要等待某些任务完成后才能继续执行其他操作。通过使用std::future
,我们可以实现线程之间的同步,确保任务完成后再获取结果并继续执行后续操作。
结果获取
std::future
提供了一种安全的方式来获取异步任务的结果。我们可以使用std::future::get()
函数来获取任务的结果。此函数会阻塞当前线程,直到异步操作完成。这样,在调用get()
函数时,我们可以确保已经获取到了所需的结果。如果异步操作中发生了异常,get()
函数会将异常重新抛出,使得我们能够处理这些异常。
用法示例
使用std::async关联异步任务
使用std::async
将任务与std::future
关联。它创建并运行一个异步任务,并返回一个与该任务结果关联的std::future
对象。
1 |
|
使用std::promise与std::future配合
std::promise
是另一种与std::future
配合使用的方式。我们可以使用std::promise
对象来显式地设置任务的结果,而std::future
对象则用于获取这个结果。
1 |
|
结果获取与异常处理
使用std::future::get()
函数可以获取异步任务的结果。此函数会阻塞当前线程,直到异步操作完成。如果异步操作中发生了异常,get()
函数会将异常重新抛出。
⚠️ 注意:std::future::get()函数只能被调用一次。在调用get()之后,std::future对象将变为无效状态。
🚧 如果需要多次访问结果,可以考虑使用std::shared_future。
1 |
|
注意事项
在使用std::future
时,需要注意以下几点:
std::future::get()
函数只能被调用一次。调用get()之后,std::future
对象将变为无效状态。如果需要多次访问结果,可以考虑使用std::shared_future。std::future
对象不可拷贝,但可以通过移动构造函数或移动赋值操作符进行转移。这意味着我们不能将std::future对象存储在容器中,除非使用std::shared_future或指针包装。- 当使用
std::async
时,默认情况下,任务可能会在当前线程上下文中执行。这取决于库实现和系统资源。若要确保任务在新线程中执行,可以使用std::launch::async
标志:
std::future result_future = std::async(std::launch::async, long_running_task);
- 如果std::future对象在析构时仍关联着一个有效的异步操作,且该操作尚未完成,析构函数会阻塞等待操作完成。在某些情况下,这可能导致程序死锁。为了避免这个问题,可以在析构std::future对象之前显式地调用wait()、wait_for()或wait_until()函数。
- 虽然
std::future
在获取异步任务结果和线程同步方面非常有用,但它并不能解决所有并发问题。例如,std::future
无法用于实现复杂的并发模式,如线程池、工作窃取等。对于这些高级并发需求,可能需要使用其他库或者自定义实现。 - 使用
std::promise
和std::future
时,需要确保在调用get()
之前已经设置了对应的值,否则会导致未定义的行为。这可能需要对代码进行仔细的设计和调试,以确保正确的执行顺序。 std::future
对象的生命周期需要仔细管理。如果std::future
对象被提前销毁,那么关联的异步任务可能会变得不可访问。因此,需要确保在异步任务完成之前保持std::future
对象的有效性。- 在某些情况下,使用
std::future
可能会导致性能下降。例如,当多个线程频繁地等待异步操作结果时,可能会导致线程阻塞和上下文切换开销。为了避免这个问题,可以考虑使用非阻塞的方式查询异步操作状态,如std::future::wait_for()
和std::future::wait_until()
函数。这样,我们可以在异步操作未完成时执行其他任务,提高程序的响应性和并发性能。 std::future
并不适用于所有场景。例如,它不适用于需要处理多个输入或输出结果的任务,或者需要实现动态任务依赖关系的场景。在这些情况下,可能需要寻找其他并发和同步解决方案。- 虽然
std::future
提供了异常处理机制,但需要注意的是,一旦std::future::get()
函数重新抛出异常,该异常需要在调用get()
的线程中进行捕获和处理。这意味着需要在主线程和其他线程中设置适当的异常处理策略,以确保程序的稳定性和健壮性。
其他
std::shared_future
std::shared_future
是std::future
的一个变体,允许多个线程共享同一个异步操作的结果。与std::future
不同,std::shared_future
对象可以被拷贝,因此可以将其存储在容器中或在多个线程之间传递。此外,std::shared_future::get()
函数可以被多次调用,而不会使std::shared_future
对象变为无效状态。
1 |
|
std::future_status
std::future_status
是一个枚举类型,表示异步操作的状态。它有三个可能的值:
std::future_status::ready
:异步操作已完成,结果可用。std::future_status::timeout
:异步操作尚未完成,等待已超时。std::future_status::deferred
:异步操作已被延迟,尚未启动(仅当使用std::launch::deferred
策略创建std::future
对象时才可能出现)。
我们可以使用std::future::wait_for()
和std::future::wait_until()
函数查询异步操作的状态,而无需阻塞当前线程。
1 |
|
在上面的示例中,我们使用了std::future::wait_for()
函数来查询异步操作的状态。如果状态为std::future_status::ready
,我们可以立即获取结果。否则,我们可以在等待期间执行其他任务,以提高程序的响应性和并发性能。最后,在退出main
函数之前,我们会调用result_future.get()
以确保正确获取异步操作的结果。