【问题标题】:When to use promise over async or packaged_task?何时在 async 或 packaged_task 上使用 promise?
【发布时间】:2013-07-17 18:50:20
【问题描述】:

什么时候应该使用std::promise 而不是std::asyncstd::packaged_task? 你能给我具体的例子说明什么时候使用它们吗?

【问题讨论】:

    标签: c++ asynchronous promise future packaged-task


    【解决方案1】:

    std::async

    std::async 是获取std::future 的一种简洁而简单的方法,但是:

    • 它并不总是启动一个新线程;枚举值std::launch::async 可以作为第一个参数传递给std::async,以确保创建一个新线程来执行func 指定的任务,从而确保func 异步执行。

        auto f = std::async( std::launch::async, func );
      
    • destructor of std::future can block until the new thread completes

        auto sleep = [](int s) { std::this_thread::sleep_for(std::chrono::seconds(s)); };
      
        {
            auto f = std::async( std::launch::async, sleep, 5 );
        }
      

    通常我们希望只有.get().wait() 阻塞,但是对于从std::async 返回的std::future,析构函数也可能阻塞,因此请注意不要因为忘记它而阻塞主线程。

    • 如果std::future 存储在临时对象中,则std::async 调用将在对象销毁时阻塞,因此如果删除@,接下来的阻塞将需要10 秒 987654343@ 初始化。它只会阻塞 5 秒 否则,因为两个睡眠将是并发的,由于在块结束时两个对象的销毁导致两者都等待完成:

        auto sleep = [](int s) { std::this_thread::sleep_for(std::chrono::seconds(s)); };
      
        {
            auto f1 = std::async( std::launch::async, sleep, 5 );
            auto f2 = std::async( std::launch::async, sleep, 5 );
        }
      

    std::packaged_task

    std::packaged_task 本身与线程无关:它只是一个函子和一个相关的std::future。考虑以下几点:

    auto task = [](int i) {
       std::this_thread::sleep_for(std::chrono::seconds(5));
       return i+100;
    };
    
    std::packaged_task< int(int) > package{ task };
    std::future<int> f = package.get_future();
    package(1);
    std::cout << f.get() << "\n";
    

    这里我们只是通过package(1) 运行任务,在它返回后,f 已准备就绪,因此不会阻塞.get()

    std::packaged_task 的一个特性使它对线程非常有用。您可以使用 std::packaged_task 初始化 std::thread 而不仅仅是一个函数,这提供了一种非常好的方式来获取“std::future”。考虑以下几点:

    std::packaged_task< int(int) > package{ task };
    std::future<int> f = package.get_future();
    std::thread t { std::move(package), 5 };
    
    std::cout << f.get() << "\n";       //block here until t finishes
    
    t.join();
    

    由于std::packaged_task 不可复制,您必须将其移动到带有std::move 的新线程。

    std::promise

    std::promise 是一个强大的机制。例如,您可以将值传递给新线程,而无需任何额外的同步。

    auto task = [](std::future<int> i) {
        std::cout << i.get() << std::flush;
    };
    
    std::promise<int> p;
    std::thread t{ task, p.get_future() };
    
    std::this_thread::sleep_for(std::chrono::seconds(5));
    p.set_value(5);
    
    t.join();
    

    新线程将在.get() 等我们


    所以,一般来说,回答你的问题:

    • std::async 仅用于简单的事情,例如做一些非阻塞调用,但请记住上面阻塞的 cmets。

    • 使用std::packaged_task 轻松获取std::future,并将其作为单独的线程运行

        std::thread{ std::move(package), param }.detach();
      

        std::thread t { std::move(package), param };
    
    • 当您需要对未来进行更多控制时,请使用 std::promise

    另请参阅std::shared_future 以及在线程之间传递异常std::promise::set_exception

    【讨论】:

      【解决方案2】:

      promise 用于存储使用例如计算的值。一个 std::async。 见http://en.cppreference.com/w/cpp/thread/promise

      我可以想象你想知道 std::packaged_task 和 std::async 之间的区别(在最常见的方法中,std::async 现在启动一个单独的线程来运行 function/lambda/etc 并且(可能)昂贵计算。 std::packaged_task 用于使用参数的当前值包装函数/lambda/etc,以便您以后可以同步或在单独的线程中运行它。

      std::packaged_task 和 std::async 都提供了一个 std::future,一旦运行,它将包含包装函数/lambda/etc 的结果。 在内部,std::future 使用 std::promise 来保存该结果。

      【讨论】:

      • std::futurestd::promise 具有“共享状态”,因此结果不会存储在 Promise 对象中,而是存储在共享状态中
      • packaged_task 不打包参数值,它只保存一个函数对象和一个“共享状态”。
      猜你喜欢
      • 2019-02-16
      • 2019-04-17
      • 2020-03-22
      • 2018-12-11
      • 1970-01-01
      • 2013-08-11
      • 1970-01-01
      • 2018-05-16
      相关资源
      最近更新 更多