我相当熟悉c++ 11的std::thread, std::async和std::future组件(例如,请看这个答案),这些都很简单。
然而,我不能完全理解std::promise是什么,它做什么以及在什么情况下最好使用它。标准文档本身除了类概要之外不包含大量信息,std::thread也不包含。
谁能给一个简要的例子,说明需要std::promise的情况,以及它是最常用的解决方案的情况?
我相当熟悉c++ 11的std::thread, std::async和std::future组件(例如,请看这个答案),这些都很简单。
然而,我不能完全理解std::promise是什么,它做什么以及在什么情况下最好使用它。标准文档本身除了类概要之外不包含大量信息,std::thread也不包含。
谁能给一个简要的例子,说明需要std::promise的情况,以及它是最常用的解决方案的情况?
当前回答
在异步处理中有3个核心实体。c++ 11目前专注于其中的两个。
异步运行逻辑所需要的核心内容是:
任务(逻辑打包为某个函子对象)将运行“某处”。 实际的处理节点——线程、进程等,当这些函子被提供给它时运行它们。查看“Command”设计模式,了解基本工作线程池是如何做到这一点的。 结果句柄:有人需要这个结果,需要一个对象来为他们获取结果。出于OOP和其他原因,任何等待或同步都应该在这个句柄的api中完成。
c++ 11调用了我在(1)std::promise和(3)std::future中提到的东西。 std::thread是(2)唯一公开提供的东西。这是不幸的,因为真正的程序需要管理线程和内存资源,大多数程序都希望任务运行在线程池上,而不是为每个小任务创建和销毁一个线程(这几乎总是会导致不必要的性能损失,并且很容易造成更糟糕的资源短缺)。
根据Herb Sutter和c++ 11智囊团的其他人的说法,他们暂定计划添加std::executor,这很像Java,将是线程池的基础,逻辑上类似于(2)的设置。也许我们会在c++ 2014中看到它,但我打赌更像c++ 17(如果他们搞砸了这些标准,上帝会帮助我们)。
其他回答
承诺是电线的另一端。
假设您需要检索由异步程序计算的future的值。然而,你不希望它在同一个线程中计算,你甚至不“现在”生成一个线程——也许你的软件被设计为从线程池中选择一个线程,所以你不知道谁将在最后执行这些计算。
现在,你传递什么给这个(未知的)线程/类/实体?你不通过将来,因为这是结果。你想要传递一些连接到未来的东西,它代表导线的另一端,所以你只会查询未来,而不知道谁会实际计算/写入一些东西。
这就是承诺。它是连接你未来的手柄。如果future是一个演讲者,并且使用get()开始听直到一些声音出来,那么promise就是一个麦克风;但不是普通的麦克风,它是通过一根电线连接到你手中的扬声器的麦克风。你可能知道电话的另一端是谁,但你不需要知道——你只需要把电话给对方,然后等对方开口。
promise是async函数返回信息的通道或路径。Std::future是一种同步机制,它使调用者等待Std::promise中携带的返回值准备好(意味着它的值在函数内部设置)。
Bartosz milwski提供了一个很好的记录。
c++将期货的实现划分为一个集合 小块的
性病:承诺是这些部分之一。
承诺是传递返回值(或对象)的载体 异常)从执行函数的线程到线程 这就利用了未来的功能。
...
对象周围构造的同步对象 承诺信道的接收端。
所以,如果你想使用一个未来,你最终会得到一个承诺,你用它来获得异步处理的结果。
该页面的一个例子是:
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
在异步处理中有3个核心实体。c++ 11目前专注于其中的两个。
异步运行逻辑所需要的核心内容是:
任务(逻辑打包为某个函子对象)将运行“某处”。 实际的处理节点——线程、进程等,当这些函子被提供给它时运行它们。查看“Command”设计模式,了解基本工作线程池是如何做到这一点的。 结果句柄:有人需要这个结果,需要一个对象来为他们获取结果。出于OOP和其他原因,任何等待或同步都应该在这个句柄的api中完成。
c++ 11调用了我在(1)std::promise和(3)std::future中提到的东西。 std::thread是(2)唯一公开提供的东西。这是不幸的,因为真正的程序需要管理线程和内存资源,大多数程序都希望任务运行在线程池上,而不是为每个小任务创建和销毁一个线程(这几乎总是会导致不必要的性能损失,并且很容易造成更糟糕的资源短缺)。
根据Herb Sutter和c++ 11智囊团的其他人的说法,他们暂定计划添加std::executor,这很像Java,将是线程池的基础,逻辑上类似于(2)的设置。也许我们会在c++ 2014中看到它,但我打赌更像c++ 17(如果他们搞砸了这些标准,上帝会帮助我们)。
在一个粗略的近似中,你可以把std::promise看作std::future的另一端(这是错误的,但是为了说明问题,你可以把它看作是)。通信通道的消费者端将使用std::future来消费来自共享状态的数据,而生产者线程将使用std::promise来写入共享状态。