【C++】future/promise

future/promise

1 来源

需要从线程中返回异步任务结果情形时,c++11之前:
使用指针在线程间共享数据。
传递一个指针到新的线程中,该线程在其中设置数据,直到主线程继续等待使用条件变量。当新线程设置数据并通知条件变量时,主线程将唤醒并从指针处获取数据;
示例:

void fun(int x, int y, int *ans)
{
    std::cout << "proc thread: " << std::hex << std::this_thread::get_id() << std::endl;
    *ans = x + y;
}

void test_future()
{
    std::cout << "main thread: " << std::hex << std::this_thread::get_id() << std::endl;

    int a{10};
    int b{8};

    int sum{};
    std::thread t(fun, a, b, &sum);

    t.join();

    std::cout << "sum: " << sum << std::endl;

    return;
}

std::future
C++11提供future类模板,future对象提供访问异步操作结果的机制。

分类
唯一期望(unique futures, std::future<>)实例只能与一个指定事件相关联;
共享期望(shared futures, std::shared_future<>)实例能关联多个事件

一个std::future对象在内部存储一个将来会被某个provider赋值的值,并提供一个访问该值的机制,通过get()成员函数实现。在get可用前访问,会被阻塞,直至该值可用。

2 std::future

一个有效的std::future对象通常由以下三种Provider创建,并和某个共享状态相关联。Provider可以是函数/类,分别为:

std::async函数
std::promise::get_future
std::packaged_task::get_future

C++11提供的future头文件中包含:

Providers类:std::promise, std::package_task
Futures类:std::future, shared_future
Providers函数:std::async
Other: std::future_error, std::future_errc, std::future_status, std::lauch

std::future提供默认构造函数和移动构造函数,禁止拷贝构造函数。

成员函数:
std::future::valid()
检查future对象是否拥有共享状态,参照构造函数只有两种可用,默认构造创建的future对象不具有共享状态,即valid() = false,除非是被move赋值;移动构造的future对象往往拥有共享状态,在get()前需要确认共享状态标志是否设置为ready。

std::future::get()
阻塞式获取共享状态的值,若调用get时,状态不是ready,则阻塞至ready。

std::future::wait()
等待共享状态标志变为ready,此前线程一直阻塞

std::future::wait_for()
等待一段时间,之后即使共享状态不为ready,一样会返回。

std::future::wait_until()
参考绝对时间点

std::future::share()
返回一个std::shared_future对象,调用该函数后,future对象不和任何共享状态相关联,也就不再是valid

[注] wait_for & wait_until返回值
future_status::ready
future_status::timeout
future_status::deferred

模板参数是void情景

在C++中,std::future 是一个模板类,它用于从异步任务中检索结果。std::future 的模板参数是用于指定异步任务返回值的类型。当异步任务不返回任何值(即返回类型为 void)时,你仍然可以使用 std::future,但此时 std::future 的模板参数应该被明确指定为 void

然而,直接使用 std::future<void> 可能并不常见,因为 std::future 的主要用途是检索异步任务的结果。当任务没有返回值时,你更可能只是关心任务是否完成,而不是它的结果。对于这种情况,std::future<void> 仍然可以工作,但你可能更想使用 std::promise<void>std::shared_future<void> 来与异步任务进行交互。

以下是一个使用 std::promise<void>std::future<void> 的简单示例:

#include <iostream>
#include <thread>
#include <future>

void async_task(std::promise<void> prom) {
    // 模拟一些异步工作
    std::this_thread::sleep_for(std::chrono::seconds(2));

    // 当工作完成时,设置 promise 的值(对于 void 类型,我们调用 set_value() 但不传递任何参数)
    prom.set_value();
}

int main() {
    // 创建一个 promise 对象
    std::promise<void> prom;
    // 从 promise 对象中获取 future 对象
    std::future<void> fut = prom.get_future();

    // 在新线程中运行异步任务
    std::thread t(async_task, std::move(prom));

    // 在主线程中等待异步任务完成
    fut.wait(); // 对于 void 类型的 future,我们只需要等待它完成,而不是检索结果

    std::cout << "Async task completed.\n";

    // 确保线程完成后再退出 main 函数
    t.join();

    return 0;
}

在这个示例中,我们创建了一个 std::promise<void> 对象,并从中获取了一个 std::future<void> 对象。然后,我们在一个新线程中运行异步任务,并在主线程中等待该任务完成。由于异步任务不返回任何值,因此我们只关心 std::future<void>wait() 方法来确保任务已经完成。

3 std::promise

提供一个不同线程间的数据同步机制,可以存储一个某种类型的值,并将其传递给对应的future,即使这个future和promise不再同一个线程中也可以安全访问该值。

通过get_future来获取与该promise对象相关联的future对象,调用该函数之后,两个对象共享相同的shared state。

set_value()用于设置共享状态的值,将promise的共享状态标志变为ready。

promise对象是异步Provider,可在某一时刻设置共享状态的值
future对象可以异步返回共享状态的值,或者在必要的情况下阻塞调用者并等待共享状态变为ready,然后获取共享状态的值。

4 std::future & std::promise example

#include <iostream>
#include <thread>
#include <future>

void func(int x, int y, std::promise<int> &promiseObj) {
	promiseObj.set_value(x + y);
}

void test() {
	int a{10};
	int b{8};
	std::promise<int> promiseObj;
	std::future<int> futureObj = promiseObj.get_future();
	std::thread t(func, a, b, std::ref(promiseObj));
	t.join();

	int sum = futureObj.get();
	std::cout << "sum= " << sum << std::endl;
	return 0;
}

5 std::shared_future

std::shared_future 是 C++11 引入的一个类,用于从多个线程中共享对某个异步任务的结果的访问。std::shared_futurestd::future 类似,但有一个重要的区别:std::shared_future 的实例可以被复制并传递给多个线程,而 std::future 的实例在被移动后就不能再被访问了。

当你有一个 std::promise 对象,并且你想让多个线程能够访问其关联的 std::future 对象以获取异步任务的结果时,std::shared_future 就变得非常有用。但是,你不能直接从 std::promise 获取 std::shared_future;你需要首先获取一个 std::future,然后使用 std::future::share 方法来创建 std::shared_future。但在实际使用中,通常通过 std::async 或其他异步操作直接获取 std::shared_future

以下是一个简单的示例,展示了如何使用 std::asyncstd::shared_future

#include <iostream>
#include <future>
#include <thread>

int compute_something(int x) {
    // 假设这是一个耗时的计算
    return x * x;
}

int main() {
    // 使用 std::async 启动一个异步任务,并获取 std::shared_future
    std::shared_future<int> result = std::async(std::launch::async, compute_something, 42);

    // 在主线程中做一些其他事情
    // ...

    // 等待异步任务完成并获取结果
    int value = result.get();
    std::cout << "The result is " << value << std::endl;

    return 0;
}

注意,虽然在这个例子中我们只在一个线程中使用了 std::shared_future,但你可以将其复制到多个线程中,并在所有线程上调用 get()。只要一个线程调用了 get()shared_future 的状态就会变为 ready,其他所有线程再调用 get() 时都会立即返回结果,而不需要等待异步任务完成。但是,如果异步任务还没有完成,并且没有线程调用 get(),那么第一个调用 get() 的线程将会阻塞,直到异步任务完成。

还要注意,尽管 std::shared_future 允许多个线程访问异步任务的结果,但它并不提供对结果的同步访问。如果你需要多个线程同时读写某个共享数据,你应该使用其他的同步机制(如互斥锁)来确保数据的一致性。

N 参考资料

https://www.cnblogs.com/linuxAndMcu/p/14577275.html

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值