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

pet*_*tke 11 c++ multithreading future promise c++11

我正在使用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;
    }

 }
Run Code Online (Sandbox Code Playgroud)

我似乎无法超越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

And*_*zos 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;
}
Run Code Online (Sandbox Code Playgroud)

在堆上分配promise,然后将[=]shared_ptr传递给lambda.

  • 哇!这样可行.如果可以的话,我会给你一百万分.你做了我的一周. (2认同)

Jon*_*ely 5

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

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

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;
}
Run Code Online (Sandbox Code Playgroud)

这将promise转移到std::thread对象中,然后将对象(在新线程的上下文中)移动到lambda的参数中p.