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;
    }

 }

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

有人有什么想法吗?

编辑:我在这里强调这一点,因为顶部评论有点误导。虽然允许 std::asycn 的默认值是 dererred 模式,但当显式设置 std::launch::async 的启动策略时,它必须表现得“好像”线程立即产生并运行(参见 en 中的措辞.cppreference.com/w/cpp/thread/async)。请参阅 pastebin.com/5dWCjjNY 中的示例,以了解这不是 vs20011 中所见行为的一种情况。该解决方案效果很好,并将我的实际应用程序加速了 10 倍。

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

4

2 回答 2

23

这是一种解决方案:

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。

于 2012-04-17T03:29:32.890 回答
6

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

但是,std::thread支持将右值对象传递给新线程的启动函数。因此您可以声明 lambda 以std::promise按值获取参数,promise即将 lambda 传递而不是捕获它,然后将 promise 移入std::threadeg的参数之一

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;
}

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

于 2013-01-07T22:57:52.710 回答