当前位置: 首页 > 知识库问答 >
问题:

什么是d::promise?

章阳波
2023-03-14

我非常熟悉C 11的std::threadstd::asyncstd::future组件(例如,请参见此答案),它们都是直截了当的。

然而,我不能完全理解什么是d::promise,它做了什么,以及在哪些情况下最好使用它。标准文档本身不包含超出类概要的大量信息,std::线程也是如此。

有人能给我举一个简短的例子,说明在什么情况下需要std::promise,它是最惯用的解决方案吗?

共有3个答案

宰父玄天
2023-03-14

巴托斯·米莱夫斯基提供了一个很好的写作。

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
关志
2023-03-14

用[futures.state]的话说,std::future是一个异步返回对象(“从共享状态读取结果的对象”),而std::promise是一个异步提供程序(“向共享状态提供结果的对象”),即promise是您设置结果的对象,这样你就可以从相关的未来得到它。

异步提供程序最初创建的是将来引用的共享状态std::promise是一种异步提供程序,std::packaged_task是另一种,而std::async的内部细节是另一种。其中每一个都可以创建一个共享状态,并为您提供一个共享该状态的std::future,并且可以使该状态就绪。

std::async是一个更高级的便利实用程序,它为您提供一个异步结果对象,并在内部负责创建异步提供程序,并在任务完成时使共享状态就绪。您可以使用std::packaged_任务(或std::bindstd::promise)和std::线程来模拟它,但它更安全、更易于使用std::async

std::promise的级别稍低,因为当您希望将异步结果传递给将来时,但使结果就绪的代码不能封装在适合传递给std::async的单个函数中。例如,您可能有一个包含多个promises和关联的futures的数组,并且有一个线程执行多个计算并设置每个promise的结果async只允许返回单个结果,要返回多个结果,需要多次调用async,这可能会浪费资源。

松秦斩
2023-03-14

我现在对情况有了更好的了解(由于这里的答案,我对情况的了解不小!),所以我想我加了一点我自己的评论。

在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.