用自己的版本替换std :: async但std :: promise应该在哪里生效?

时间:2012-04-17 02:42:15

标签: c++ multithreading c++11 future promise

我正在使用vc2011,结果是std :: async(std :: launch :: async,...)有点儿错误(有时它不会产生新的线程并且并行运行它们,而是重用线程并一个接一个地运行任务)。当我进行昂贵的网络呼叫时,这太慢了。所以我想我会编写自己的异步函数。我虽然陷入困境,std :: promise应该在哪里生活?在1)线程函数中,2)异步函数,或3)调用函数。

代码:

#include <future>
#include <thread>
#include <iostream>
#include <string>
#include <vector>

std::string thFun() {
    throw std::exception("bang!");
    return "val";
}

std::future<std::string> myasync(std::promise<std::string>& prms) {
//std::future<std::string> myasync() {
    //std::promise<std::string> prms; //needs to outlive thread. How?

    std::future<std::string> fut = prms.get_future();
    std::thread th([&](){
        //std::promise<std::string> prms; //need to return a future before...
        try {
            std::string val = thFun();

            prms.set_value(val);

        } catch(...) {
            prms.set_exception(std::current_exception());
        }

     });

    th.detach();
    return fut;
}

 int main() {

    std::promise<std::string> prms; //I really want the promise hidden iway in the myasync func and not live here in caller code but the promise needs to outlive myasync and live as long as the thread. How do I do this?
    auto fut = myasync(prms);

    //auto fut = myasync(); //Exception: future already retrieved

    try {
        auto res = fut.get();
        std::cout << "Result: " << res << std::endl;

    } catch(const std::exception& exc) {
        std::cout << "Exception: " << exc.what() << std::endl;
    }

 }

我似乎无法超越这样一个事实:std :: promise需要比异步函数更长(并且与线程一样长),所以promise不能作为async func中的局部变量。但是std :: promise也不应该存在于调用者代码中,因为调用者只需要知道未来。而且我不知道如何在线程函数中生成承诺,因为async需要在它调用线程函数之前返回未来。我在这个问题上摸不着头脑。

有人有任何想法吗?

编辑:我在这里强调这一点,因为最高评论有点误导。虽然允许std :: asycn的默认值为dererred模式,但是当显式设置std :: launch :: async的启动策略时,它必须表现为“好像”线程一次生成并运行(参见en中的措辞) .cppreference.com / W / CPP /线程/异步)。请参阅pastebin.com/5dWCjjNY中的示例,了解一个不是vs20011中的行为的情况。该解决方案效果很好,加速了我的真实世界应用程序10倍。

编辑2:MS修复了这个bug。更多信息:https://connect.microsoft.com/VisualStudio/feedback/details/735731/std-async-std-launch-async-does-not-behave-as-std-thread

2 个答案:

答案 0 :(得分:22)

这是一个解决方案:

future<string> myasync()
{
    auto prms = make_shared<promise<string>> ();

    future<string> fut = prms->get_future();

    thread th([=](){

        try {
            string val = thFun();
            // ...
            prms->set_value(val);

        } catch(...) {
            prms->set_exception(current_exception());
        }

     });

    th.detach();

    return fut;
}

在堆上分配promise,然后将shared_ptr传递给lambda,通过值[=]传递给lambda。

答案 1 :(得分:5)

您需要将promise转移到新线程中。 Andrew Tomazos的答案是通过创建一个共享所有权的std::promise来做到这一点,这样两个线程都可以拥有这个承诺,当前一个线程从当前范围返回时,只有新线程拥有承诺,即所有权已经转移。但是std::promise是可移动的,因此应该可以将它直接移动到新线程中,除了捕获它的“明显”解决方案不起作用,因为lambda不能通过移动捕获,只能通过复制(或通过引用,这将无法正常工作,因为你会得到一个悬空参考。)

但是,std::thread支持将rvalue对象传递给新线程的start函数。所以你可以声明lambda按值获取std::promise参数,即将promise传递给lambda而不是捕获它,然后将promise转移到{{1}的一个参数中例如

std::thread

将promise转移到std::future<std::string> myasync() { std::promise<std::string> prms; std::future<std::string> fut = prms.get_future(); std::thread th([&](std::promise<std::string> p){ try { std::string val = thFun(); p.set_value(val); } catch(...) { p.set_exception(std::current_exception()); } }, std::move(prms)); th.detach(); return fut; } 对象,然后将其移动(在新线程的上下文中)到lambda的参数std::thread