我非常熟悉C ++ 11的std::thread
,std::async
和std::future
组件(例如参见this answer),这些组件很简单。
但是,我无法完全理解std::promise
是什么,它做了什么以及在哪种情况下最好使用它。标准文档本身不包含其类概要之外的大量信息,just::thread也没有。
有人可以给出一个简短,简洁的例子,说明需要std::promise
的情况以及最惯用的解决方案吗?
答案 0 :(得分:167)
用[futures.state]的话说,std::future
是异步返回对象(“从共享状态读取结果的对象”)和std::promise
是一个异步提供程序(“一个向共享状态提供结果的对象”),即一个promise是设置结果的东西,因此你可以< em>从相关的未来中获取。
异步提供程序最初创建未来引用的共享状态。 std::promise
是一种异步提供程序,std::packaged_task
是另一种,std::async
的内部详细信息是另一种。其中每个都可以创建一个共享状态,并为您提供一个共享该状态的std::future
,并且可以使状态准备就绪。
std::async
是一个更高级别的便捷实用程序,它为您提供异步结果对象,并在内部负责创建异步提供程序并在任务完成时使共享状态准备就绪。您可以使用std::packaged_task
(或std::bind
和std::promise
)以及std::thread
来模拟它,但使用std::async
会更安全,更容易。
std::promise
有点低级,因为当你想要将异步结果传递给将来时,但是使得结果准备好的代码不能被包装在适合传递给{{的单个函数中。 1}}。例如,您可能有一个包含多个std::async
和相关promise
的数组,并且有一个线程可以执行多次计算并在每个promise上设置结果。 future
只允许您返回一个结果,返回几次需要多次调用async
,这可能会浪费资源。
答案 1 :(得分:31)
Bartosz Milewski提供了很好的写作。
C ++将期货的实施分成一组 小块
std :: promise是这些部分之一。
承诺是传递返回值的工具(或者 异常)从执行函数的线程到线程 在功能的未来充实。
...
未来是围绕着构建的同步对象 接收承诺通道的终点。
因此,如果您想使用未来,最终会得到一个用于获取异步处理结果的承诺。
该页面的一个例子是:
promise<int> intPromise;
future<int> intFuture = intPromise.get_future();
std::thread t(asyncFun, std::move(intPromise));
// do some other stuff
int result = intFuture.get(); // may throw MyException
答案 2 :(得分:26)
在粗略的近似中,您可以将std::promise
视为std::future
的另一端(这是 false ,但为了说明您可以认为它就像)。通信通道的消费者端将使用std::future
来使用共享状态中的数据,而生产者线程将使用std::promise
来写入共享状态。
答案 3 :(得分:10)
std::promise
是从异步函数返回信息的通道或途径。 std::future
是同步机制,它使调用者等待,直到std::promise
中携带的返回值准备好(意味着它的值在函数内部设置)。
答案 4 :(得分:7)
异步处理中确实有3个核心实体。 C ++ 11目前主要关注其中的两个。
异步运行某些逻辑所需的核心内容是:
C ++ 11在(1)std::promise
和(3)std::future
中调用我所说的内容。
std::thread
是(2)公开提供的唯一内容。这是不幸的,因为真正的程序需要管理线程和内存资源,大多数都希望任务在线程池上运行,而不是创建&amp;为每一个小任务摧毁一个线程(这几乎总是会导致不必要的性能命中,并且很容易造成更糟糕的资源匮乏)。
根据Herb Sutter和C ++ 11脑信任中的其他人的说法,有一些暂定计划添加一个std::executor
- 就像在Java中一样 - 将成为线程池的基础和逻辑上类似的设置( 2)。也许我们会在C ++ 2014中看到它,但我的赌注更像是C ++ 17(如果他们为这些标准制定标准,上帝会帮助我们)。
答案 5 :(得分:6)
创建std::promise
作为promise / future对的终点,std::future
(使用get_future()
方法从std :: promise创建)是另一个终点。这是一种简单的一次性方法,为两个线程提供同步方式,因为一个线程通过消息向另一个线程提供数据。
您可以将其视为一个线程创建提供数据的承诺,而另一个线程将来收集承诺。这种机制只能使用一次。
承诺/未来机制只是一个方向,从使用set_value()
的{{1}}方法的线程到使用std::promise
的{{1}}的线程接收数据。如果未来的get()
方法被多次调用,则会生成异常。
如果具有std::future
的线程未使用get()
来履行其承诺,那么当第二个线程调用std::promise
的{{1}}来收集承诺时,第二个线程线程将进入等待状态,直到使用set_value()
方法发送数据的第一个线程get()
履行承诺。
使用Technical Specification N4663 Programming Languages — C++ Extensions for Coroutines提议的协同程序和std::future
的Visual Studio 2017 C ++编译器支持,还可以使用std::promise
和set_value()
来编写协同程序功能。请参阅https://stackoverflow.com/a/50753040/1466970中的讨论和示例,其中有一节讨论co_await
与std::future
的使用。
以下示例代码是一个简单的Visual Studio 2013 Windows控制台应用程序,它使用了一些C ++ 11并发类/模板和其他功能。它说明了promise / future的使用效果很好,自治线程可以执行某些任务和停止,以及需要更多同步行为的用途,并且由于需要多个通知,promise / future对不起作用。
关于这个例子的一个注意事项是在各个地方添加了延迟。添加这些延迟只是为了确保使用std::async
打印到控制台的各种消息是明确的,并且来自多个线程的文本不会混合。
std::future
的第一部分是创建三个额外的线程,并使用co_await
和std::cout
在线程之间发送数据。一个有趣的点是主线程启动一个线程T2,它将等待来自主线程的数据,执行某些操作,然后将数据发送到第三个线程T3,然后T3将执行某些操作并将数据发送回主线。
main()
的第二部分创建了两个线程和一组队列,以允许从主线程到两个创建的线程中的每一个的多条消息。我们不能使用std::promise
和std::future
,因为承诺/未来二人组是一次性的,不能重复使用。
类main()
的源代码来自Stroustrup的The C ++ Programming Language:4th Edition。
std::promise
这个简单的应用程序会创建以下输出。
std::future
答案 6 :(得分:1)
承诺是电汇的另一端。
想象一下,您需要检索由future
计算的async
的值。但是,你不希望它在同一个线程中计算,你甚至不会产生一个线程&#34;现在&#34; - 也许你的软件设计用于从池中挑选一个线程,所以你不知道谁最终会执行计算。
现在,你传递给这个(未知的)线程/类/实体是什么?您没有通过future
,因为这是结果。您希望将已连接的内容传递给future
,并代表线路的另一端,因此您只需查询future
不了解谁将实际计算/写一些东西。
这是promise
。这是与您的future
相关联的句柄。如果future
是发言人,并且get()
您开始收听,直到某些声音响起,promise
就是麦克风 ;但不只是任何麦克风, 麦克风通过单根线连接到您握住的扬声器。你可能知道谁在另一端,但你不需要知道它 - 你只需要给它并等到另一方说出来。
答案 7 :(得分:0)
http://www.cplusplus.com/reference/future/promise/
一句话解释: furture :: get()永远等待promse :: set_value()。
void print_int(std::future<int>& fut) {
int x = fut.get(); // future would wait prom.set_value forever
std::cout << "value: " << x << '\n';
}
int main()
{
std::promise<int> prom; // create promise
std::future<int> fut = prom.get_future(); // engagement with future
std::thread th1(print_int, std::ref(fut)); // send future to new thread
prom.set_value(10); // fulfill promise
// (synchronizes with getting the future)
th1.join();
return 0;
}