首页 > 编程笔记 > C++笔记 阅读:27

C++ future和promise的用法(附带实例)

C++ 程序中,线程函数是无法返回值的,线程需要使用共享数据等其他方式来实现。然而,这需要同步。

将返回值或异常传递给主线程或其他线程的另一种方式是使用 std::promise。本节将解释这个机制是如何工作的。

本节使用的 promise 和 future 类在 <future> 头文件的 std 命名空间中可用。

C++ promise和future的使用方式

通过 promise 和 future,从一个线程中将值传递给另一个线程,可以这么做:
1) 将 promise 变量作为线程函数的一个参数,比如:
void produce_value(std::promise<int>& p)
{
    // simulate long running operation
    {
        using namespace std::chrono_literals;
        std::this_thread::sleep_for(2s);
    }
    // continued at 2.
}

2) 在 promise 上调用 set_value() 设置值或调用 set_exception() 设置异常:
// continued from 1.
p.set_value(42);
}

3) 将 future 与 promise 关联,并将其作为参数对其他线程函数可见,示例如下:
void consume_value(std::future<int>& f)
{
    // continued at 4.
}

4) 在 future 对象上调用 get() 来获取 promise 设置的值:
// continued from 3.
auto value = f.get();

5) 在调用线程中,使用 promise 上的 get_future() 来获取与之关联的 future:
std::promise<int> p;
std::thread t1(produce_value, std::ref(p));
std::future<int> f = p.get_future();
std::thread t2(consume_value, std::ref(f));

t1.join();
t2.join();

C++ promise和future的工作原理

基本上,promise-future 对是通过共享状态,来帮助线程间传递值或异常的一种沟通通道。promise 是值的异步提供者,有相对应的 future 用来表示异步的返回对象。

为了建立这个通道,你需要先创建 promise,然后创建共享状态,稍后可通过与 promise 关联的 future 读取。

你可使用以下任意方法,来设置 promise 的结果:
1) set_value() 或 set_value_at_thread_exit() 方法用来设置返回值,后者将值保存在共享状态,但只有当线程退出时才可通过关联的 future 获取。

2) set_exception() 或 set_exception_at_thread_exit() 方法用来设置异常返回值。异常封装在 std::exception_ptr 对象中。后者将异常保存在共享状态,但只有当线程退出时才可通过关联的 future 获取。

使用 get_future() 方法,可获取与 promise 关联的 future 对象。使用 get() 方法,可从 future 中获取值。在共享状态值可用前,调用线程都将被阻塞。

future 类提供了几个方法,这些方法在共享状态值可用前,都将阻塞线程:
如果 promise 被设置了异常值,在 future 对象上调用 get() 方法将抛出异常。重写前面的示例如下,抛出异常而不是设置值:
void produce_value(std::promise<int>& p)
{
    // simulate long running operation
    {
        using namespace std::chrono_literals;
        std::this_thread::sleep_for(2s);
    }
    try
    {
        throw std::runtime_error("an error has occurred!");
    }
    catch(...)
    {
        p.set_exception(std::current_exception());
    }
}

void consume_value(std::future<int>& f)
{
    std::lock_guard<std::mutex> lock(g_mutex);
    try
    {
        std::cout << f.get() << '\n';
    }
    catch(std::exception const & e)
    {
        std::cout << e.what() << '\n';
    }
}
在 consume_value() 函数中可以看到,get() 的调用放在 try...catch 代码块中。如果异常被捕获——在这个特定的实现中——消息将被输出到控制台。

通过这种方式建立 promise-future 通道是显式的操作,可通过 std::async() 函数来避免。它是更高层级的工具,异步运行函数,创建内部 promise 并共享状态,返回关联的 future。

相关文章