web-dev-qa-db-ja.com

std :: promiseを他のstd :: threadメカニズムよりも使用するのはいつ良い考えですか?

使用する適切な_std::thread_クラスを決定するのに役立つ、いくつかのヒューリスティックを確立しようとしています。

私が理解しているように、最高レベル(最も使いやすいが柔軟性が最も低い)から最低レベルまで、次のようになります。

  1. std :: async with/std :: future(std :: shared_future)(1回限りの使い捨てプロデューサースレッド非同期で実行する場合)
  2. std :: packages_task(プロデューサーを割り当てたいが、スレッドへの呼び出しを延期する場合)
  3. std :: promise(???)

いつ使用するか最初の2つは十分に理解していると思いますが、_std::promise_についてはまだ不明です。

_std::future_を_std::async_呼び出しと組み合わせて、生成するコールバック/ファンクター/ラムダを非同期呼び出しに効果的に変換します(定義上、すぐに戻ります)。単一のコンシューマーは、ブロッキング呼び出しであるstd::future::get()を呼び出して、結果を取り戻すことができます。

_std::shared_future_は、複数のコンシューマーを許可するバージョンです。

_std::future_値をプロデューサーコールバックにバインドする場合、ただし、実際の呼び出しを後で延期する場合(タスクをスポーンスレッドに関連付ける場合)、_std::packaged_task_は正しい選択です。しかし、現在、対応する_std::future_は_std::package_task_にアクセスできるため、通常、複数のスレッドからアクセスできるため、_std::mutex_の使用に注意する必要があります。 _std::async_を使用すると、最初のケースでは、ロックについて心配する必要がないことに注意してください。

promiseに関するいくつかの興味深いリンク を読んだので、そのメカニズムとその設定方法は理解できたと思いますが、私の質問は、他の3つよりもpromiseを使用することを選択するのはいつですか?

リンクの回答(例:stdを使用)ではなく、経験則(上記の3.の???を入力)のようなアプリケーションレベルの回答を探しています。 ::いくつかのライブラリメカニズムを実装することを約束します)、_std::thread_。の初心者ユーザーに適切なクラスを選択する方法をより簡単に説明できます

言い換えれば、_std::promise_で何ができるのか、他のメカニズムではcannotできるという便利な例があると便利です。

[〜#〜] answer [〜#〜]

_std::future_は奇妙な獣です。一般に、その値を直接変更することはできません。

その値を変更できる3つのプロデューサーは次のとおりです。

  1. 非同期コールバックを介した_std::async_。これにより、_std::future_インスタンスが返されます。
  2. _std::packaged_task_は、スレッドに渡されると、そのコールバックを呼び出し、それによってその_std::future_に関連付けられた_std::packaged_task_インスタンスを更新します。このメカニズムにより、プロデューサーの早期バインドが可能になりますが、後で呼び出すことができます。
  3. _std::promise_。これにより、関連する_std::future_をset_value()呼び出しで変更できます。 _std::future_の変更を直接制御することで、複数のプロデューサーが存在する場合にデザインがスレッドセーフであることを確認する必要があります(必要に応じて_std::mutex_を使用します)。

私は思う SethCarnegieの答え

簡単に考えると、値を返すか、promiseを使用して未来を設定できます。 futureには決まった方法がありません。その機能はpromiseによって提供されます。

いつpromiseを使用するかを明確にするのに役立ちます。ただし、使用法によっては、promiseにさまざまなスレッドからアクセスできる可能性があるため、_std::mutex_が必要になる場合があることに注意する必要があります。

また、 DavidのRodriguezの答え も優れています:

通信チャネルのコンシューマー側はstd :: futureを使用して共有状態からデータを消費し、プロデューサースレッドはstd :: promiseを使用して共有状態に書き込みます。

しかし、別の方法として、結果のstlコンテナーで_std::mutex_を使用し、コンテナーを操作するために1つのスレッドまたはプロデューサーのスレッドプールを使用しないのはなぜですか?代わりに、_std::promise_を使用すると、結果のstlコンテナーと比較して、読みやすさが向上するだけでなく、何を購入できますか?

コントロールは、_std::promise_バージョンの方が優れているようです。

  1. wait()は、結果が生成されるまで、指定された未来をブロックします
  2. プロデューサースレッドが1つしかない場合、ミューテックスは必要ありません

次のグーグルテストはhelgrindとdrdの両方に合格し、単一のプロデューサーで、wait()を使用して、ミューテックスが不要であることを確認します。

[〜#〜] test [〜#〜]

_static unsigned MapFunc( std::string const& str ) 
{ 
    if ( str=="one" ) return 1u; 
    if ( str=="two" ) return 2u; 
    return 0u;
}

TEST( Test_future, Try_promise )
{
    typedef std::map<std::string,std::promise<unsigned>>  MAP; 
    MAP          my_map;

    std::future<unsigned> f1 = my_map["one"].get_future();
    std::future<unsigned> f2 = my_map["two"].get_future();

    std::thread{ 
        [ ]( MAP& m )
        { 
            m["one"].set_value( MapFunc( "one" ));
            m["two"].set_value( MapFunc( "two" ));
        }, 
      std::ref( my_map ) 
    }.detach();

    f1.wait();
    f2.wait();

    EXPECT_EQ( 1u, f1.get() );
    EXPECT_EQ( 2u, f2.get() );
}
_
21
kfmfe04

他のpromiseの代わりにを使用することを選択せず​​、promiseを使用してfulfilla future他の人と組み合わせてcppreference.com のコードサンプルは、4つすべてを使用する例を示しています。

#include <iostream>
#include <future>
#include <thread>

int main()
{
    // future from a packaged_task
    std::packaged_task<int()> task([](){ return 7; }); // wrap the function
    std::future<int> f1 = task.get_future();  // get a future
    std::thread(std::move(task)).detach(); // launch on a thread

    // future from an async()
    std::future<int> f2 = std::async(std::launch::async, [](){ return 8; });

    // future from a promise
    std::promise<int> p;
    std::future<int> f3 = p.get_future();
    std::thread( [](std::promise<int>& p){ p.set_value(9); }, 
                 std::ref(p) ).detach();

    std::cout << "Waiting...";
    f1.wait();
    f2.wait();
    f3.wait();
    std::cout << "Done!\nResults are: "
              << f1.get() << ' ' << f2.get() << ' ' << f3.get() << '\n';
}

プリント

待っています...完了です!

結果は次のとおりです。789

先物は、結果を取得するために3つのスレッドすべてで使用され、promiseは、戻り値以外の手段でfutureを満たすために3番目のスレッドで使用されます。また、単一のスレッドは、futuresを介して異なる値を持つ複数のpromisesを満たすことができますが、それ以外の場合は実行できません。

簡単に考えると、値を返すか、futureを使用してpromiseを設定できます。 futureにはsetメソッドがありません。その機能はpromiseによって提供されます。状況が許すものに基づいて、必要なものを選択します。

20
Seth Carnegie

2つのレベルの非同期がある場合は、promiseを使用する必要があります。例えば:

void fun()
{
std::promise<int> p;
std::future<int> f = p.get_future();
std::future<void> f2;
auto f3 = std::async([&]{
   // Do some other computation
   f2 = std::async([&]{ p.set_value(42);});
   // Do some other work
});
   // Do some other work
   // Now wait for the result of async work;
std::cout << f.get();
   // Do some other work again 
// Wait for async threads to complete
f3.wait();
f2.wait();
}
2
balki