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が非同期関数よりも長生きする必要がある(そしてスレッドが存続する限り存続する)必要があるという事実を乗り越えることができないようです。そのため、promiseは非同期関数のローカル変数として存続できません。ただし、呼び出し元は先物について知っているだけでよいため、std::promiseは呼び出し元のコードにも存在しないはずです。そして、非同期がスレッド関数を呼び出す前にfutureを返す必要があるため、promiseをスレッド関数で有効にする方法がわかりません。私はこれに頭を悩ませています。

誰かアイデアがありますか?

編集:トップコメントが少し誤解されているので、ここでこれを強調しています。std :: asycnのデフォルトは遅延モードであることが許可されていますが、std :: launch :: asyncの起動ポリシーが明示的に設定されている場合、スレッドが生成されて一度に実行されるかのように動作する必要があります(enの文言を参照) .cppreference.com / w / cpp / thread / async)。これがvs20011で見られる動作ではない1つのケースについては、pastebin.com/5dWCjjNYの例を参照してください。このソリューションはうまく機能し、実際のアプリケーションを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

これが1つの解決策です:

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を値渡ししてラムダに渡します。

于 2012-04-17T03:29:32.890 に答える
6

promiseを新しいスレッドに移動する必要があります。Andrew Tomazosの答えはstd::promise、共有所有権を持つwithを作成することでそれを行います。これにより、両方のスレッドがPromiseを所有でき、現在のスレッドが現在のスコープから戻ると、新しいスレッドのみがPromiseを所有します。つまり、所有権が譲渡されます。ただしstd::promise、移動可能であるため、新しいスレッドに直接移動できるはずです。ただし、ラムダは移動ではキャプチャできず、コピー(または参照)ではキャプチャできないため、キャプチャの「明らかな」ソリューションは機能しません。ぶら下がっている参照を取得するので機能しません。)

ただし、std::thread右辺値オブジェクトを新しいスレッドの開始関数に渡すことはサポートされています。std::promiseしたがって、ラムダを宣言して値で引数を取ることができます。つまりpromise、ラムダをキャプチャするのではなくラムダに渡してから、promiseをstd::threadegの引数の1つに移動できます。

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オブジェクトに移動され、次に(新しいスレッドのコンテキストで)ラムダのパラメーターに移動されますp

于 2013-01-07T22:57:52.710 に答える