我非常熟悉C 11的std::thread
、std::async
和std::future
组件(例如,请参见此答案),它们都是直截了当的。
然而,我不能完全理解什么是d::promise,它做了什么,以及在哪些情况下最好使用它。标准文档本身不包含超出类概要的大量信息,std::线程也是如此。
有人能给我举一个简短的例子,说明在什么情况下需要std::promise
,它是最惯用的解决方案吗?
巴托斯·米莱夫斯基提供了一个很好的写作。
C将期货的实现拆分为一组小块
promise就是其中之一。
promise是将返回值(或异常)从执行函数的线程传递给兑现函数未来的线程的工具。
...
未来是围绕promise信道的接收端构造的同步对象。
因此,如果您想使用未来,您最终会得到一个用于获得异步处理结果的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
用[futures.state]的话说,std::future
是一个异步返回对象(“从共享状态读取结果的对象”),而std::promise
是一个异步提供程序(“向共享状态提供结果的对象”),即promise是您设置结果的对象,这样你就可以从相关的未来得到它。
异步提供程序最初创建的是将来引用的共享状态std::promise
是一种异步提供程序,std::packaged_task
是另一种,而std::async
的内部细节是另一种。其中每一个都可以创建一个共享状态,并为您提供一个共享该状态的std::future
,并且可以使该状态就绪。
std::async
是一个更高级的便利实用程序,它为您提供一个异步结果对象,并在内部负责创建异步提供程序,并在任务完成时使共享状态就绪。您可以使用std::packaged_任务
(或std::bind
和std::promise
)和std::线程
来模拟它,但它更安全、更易于使用std::async
。
std::promise
的级别稍低,因为当您希望将异步结果传递给将来时,但使结果就绪的代码不能封装在适合传递给std::async
的单个函数中。例如,您可能有一个包含多个promise
s和关联的future
s的数组,并且有一个线程执行多个计算并设置每个promise的结果async
只允许返回单个结果,要返回多个结果,需要多次调用async
,这可能会浪费资源。
我现在对情况有了更好的了解(由于这里的答案,我对情况的了解不小!),所以我想我加了一点我自己的评论。
在C 11中有两个不同但相关的概念:异步计算(在其他地方调用的函数)和并发执行(线程,并发工作的东西)。这两个概念有些正交。异步计算只是函数调用的另一种风格,而线程是执行上下文。线程本身是有用的,但是为了讨论的目的,我将把它们作为一个实现细节。
异步计算有一个抽象层次。举个例子,假设我们有一个接受一些参数的函数:
int foo(double, char, bool);
首先,我们有一个模板std::future
现在,转到层次结构,从最高级别到最低级别:
>
std::async
:执行异步通信最方便、最直接的方法是通过async
函数模板,该模板立即返回匹配的未来:
auto fut = std::async(foo, 1.5, 'x', false); // is a std::future<int>
我们几乎无法控制细节。特别是,我们甚至不知道该函数是在
get()
上并发、串行执行的,还是由其他黑魔法执行的。然而,在需要时,很容易获得结果:
auto res = fut.get(); // is an int
现在我们可以考虑如何实现“代码> ASYNC/<代码>之类的东西,但是以我们控制的方式。例如,我们可以坚持在单独的线程中执行函数。我们已经知道,我们可以通过
std::thread
类提供一个单独的线程。
下一个较低的抽象级别正是这样做的:
std::packaged_task
。这是一个包装函数并为函数返回值提供未来的模板,但对象本身是可调用的,调用它由用户自行决定。我们可以这样设置:
std::packaged_task<int(double, char, bool)> tsk(foo);
auto fut = tsk.get_future(); // is a std::future<int>
一旦我们调用任务并且调用完成,未来就准备好了。这是se-pa-rate线程的理想工作。我们只需确保将任务移动到线程中:
std::thread thr(std::move(tsk), 1.5, 'x', false);
线程立即开始运行。我们可以分离它,或者在作用域的末尾加入它(例如使用Anthony Williams的scoped_thread包装器,它确实应该在标准库中)。不过,使用
std::线程
的细节在这里并不重要;只要确保最终连接或分离thr
。重要的是,每当函数调用结束时,我们的结果就准备好了:
auto res = fut.get(); // as before
现在我们到了最低级别:我们将如何实现打包任务?这就是
std::promise
的用武之地。promise是与未来沟通的基石。主要步骤如下:
>
调用线程从promise中获得未来。
promise和函数参数一起被移动到一个单独的线程中。
新线程执行函数并履行promise。
原始线程检索结果。
例如,我们自己的“打包任务”:
template <typename> class my_task;
template <typename R, typename ...Args>
class my_task<R(Args...)>
{
std::function<R(Args...)> fn;
std::promise<R> pr; // the promise of the result
public:
template <typename ...Ts>
explicit my_task(Ts &&... ts) : fn(std::forward<Ts>(ts)...) { }
template <typename ...Ts>
void operator()(Ts &&... ts)
{
pr.set_value(fn(std::forward<Ts>(ts)...)); // fulfill the promise
}
std::future<R> get_future() { return pr.get_future(); }
// disable copy, default move
};
该模板的用法与
std::packaged_task
基本相同。请注意,移动整个任务包含移动promise。在更特别的情况下,人们也可以将一个promise对象显式地移动到新线程中,并使其成为线程函数的函数参数,但是像上面这样的任务包装器似乎是一个更灵活、更少侵入性的解决方案。
promise与例外情况密切相关。单靠promise的接口不足以完全传递其状态,因此每当对promise的操作没有意义时,就会抛出异常。所有异常类型均为
std::future_error
,它源自std::logic_error
。首先,描述一些约束条件:
>
默认构造的promise是无效的。消极的promise可以毫无后果地死去。
当通过
get_future()
获得未来时,promise变得活跃。但是,可能只能得到一个未来!
promise必须通过
set_value()
来满足,或者在其生命周期结束之前通过set_exception()
设置异常,如果要使用其未来。一个满意的promise可以毫无结果地死去,并且get()
在未来变得可用。带有异常的promise将在未来调用get()
时引发存储的异常。如果promise在既没有值也没有异常的情况下死亡,在未来调用get()
将引发一个“违背promise”异常。
这里有一个小测试系列来展示这些不同的特殊行为。一、线束:
#include <iostream>
#include <future>
#include <exception>
#include <stdexcept>
int test();
int main()
{
try
{
return test();
}
catch (std::future_error const & e)
{
std::cout << "Future error: " << e.what() << " / " << e.code() << std::endl;
}
catch (std::exception const & e)
{
std::cout << "Standard exception: " << e.what() << std::endl;
}
catch (...)
{
std::cout << "Unknown exception." << std::endl;
}
}
现在开始测试。
案例1:无效promise
int test()
{
std::promise<int> pr;
return 0;
}
// fine, no problems
案例2:主动promise,未使用
int test()
{
std::promise<int> pr;
auto fut = pr.get_future();
return 0;
}
// fine, no problems; fut.get() would block indefinitely
案例三:期货太多
int test()
{
std::promise<int> pr;
auto fut1 = pr.get_future();
auto fut2 = pr.get_future(); // Error: "Future already retrieved"
return 0;
}
案例4:履行promise
int test()
{
std::promise<int> pr;
auto fut = pr.get_future();
{
std::promise<int> pr2(std::move(pr));
pr2.set_value(10);
}
return fut.get();
}
// Fine, returns "10".
案例五:满足感太强
int test()
{
std::promise<int> pr;
auto fut = pr.get_future();
{
std::promise<int> pr2(std::move(pr));
pr2.set_value(10);
pr2.set_value(10); // Error: "Promise already satisfied"
}
return fut.get();
}
如果存在多个
set\u值
或set\u异常
,则会引发相同的异常。
案例6:例外
int test()
{
std::promise<int> pr;
auto fut = pr.get_future();
{
std::promise<int> pr2(std::move(pr));
pr2.set_exception(std::make_exception_ptr(std::runtime_error("Booboo")));
}
return fut.get();
}
// throws the runtime_error exception
案例七:失信
int test()
{
std::promise<int> pr;
auto fut = pr.get_future();
{
std::promise<int> pr2(std::move(pr));
} // Error: "broken promise"
return fut.get();
}
我的程序运行时的很大一部分是专用于d::__detail::_Map_base,我不知道这是指什么。 我在互联网上搜索了一下,它似乎是某种结构,但我不知道它是如何占用时间的,更不用说它的五分之一了。我应该寻找什么来减少这一时间?
问题内容: 我有一个从jQuery调用的.NET网络方法。该方法返回一些我在DIV元素中显示的HTML标记。 得到回应后,我就会使用 我的问题是,.d做什么?我不喜欢使用我不完全理解的代码?使用Eval可以得到相同的结果吗? 问题答案: 您是指ADO.NET数据服务吗? 我记得曾听过有关JSON返回此内容的演示,并且我认为它只是一个包装器,以 确保有效载荷是一个JSON对象 ,而不是一个数组(返回
问题内容: 我正在学习Go,并且一直沉迷于Go旅游(exercise- stringer.go:https : //tour.golang.org/methods/7)。 这是一些代码: 所以我想出了is 的内部表示,所以散布算子起作用了。但我得到: 有没有搞错?字符串切片也不起作用,这是怎么回事? 编辑 :对不起,我的问题中有一个错误- 错误是关于type的,不是。我在玩代码,并且粘贴了错误的输
问题内容: D在什么 设置系统属性值。 在的Java应用程序启动立场?由于某种原因,它一直困扰着我,为什么是D? 问题答案: 我一直认为它是 定义 属性的值……可能是C编译器的遗留物,通常与代码中的用法相似。 编辑:目前,我对此来源最接近的是一些JDK 1.1文档 ,该文档将标志指定为: 重新定义 属性值。propertyName是要更改其值的属性的名称,而newValue是要将其更改为的值。[…
问题内容: 在这种情况下,我不得不学习新技术并运行新技术而又没有时间学习基础! 我有以下js函数调用PrintService,该函数向我返回要注入div的HTML: 在另一个例子中我注意到“ .d”之前,我为此苦苦挣扎 因此,它可以工作-但是为什么呢?这是什么“ .d”? 抱歉,如果这是一个菜鸟问题,但google不是我的朋友。 谢谢 编辑:Magnar是正确的,它是.NET特定的东西。 在此处查
我想知道为什么我们需要std::promise和std::future?为什么c 11标准将get和set_ value分为两个独立的类std::future和std::promise?在这篇文章的回答中,它提到: 它被分成这两个独立的“接口”的原因是为了对“消费者/阅读器”隐藏“写入/设置”功能。 我不明白躲在这里的好处。但是,如果我们只有一个类的“未来”,那不是更简单吗?例如:promise.