24

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

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

  1. std::async with/std::future (std::shared_future) (1 回限りのスロー アウェイ プロデューサー スレッド非同期で実行する場合)
  2. std::packaged_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 を使用することを選択するのはいつですか?

リンクの答えとは対照的に、経験則(上記の3.で???を埋める)のようなアプリケーションレベルの答えをもっと探しています(たとえば、 std::promise を使用していくつかのライブラリを実装します)メカニズム)、したがって、適切なクラスを選択する方法を の初心者ユーザーに簡単に説明できますstd::thread

言い換えれば、他のメカニズムではstd::promiseできなくて、私が でできることの有用な例があればいいのにと思います。

答え

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

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

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

SethCarnegieの答えだと思います:

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

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

また、デビッドのロドリゲスの答えも優れています。

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

しかし、別の方法として、単純std::mutexに結果の stl コンテナーで a を使用し、プロデューサーの 1 つのスレッドまたはスレッドプールをコンテナーで動作させないのはなぜでしょうか? std::promise結果の stl コンテナーと比べて読みやすさが向上する以外に、代わりに を使用すると何が得られるのでしょうか?

コントロールはstd::promiseバージョンの方が優れているようです:

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

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

テスト

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() );
}
4

2 に答える 2

22

他のもののpromise 代わりにa を使用することを選択するのではなく、他のものと組み合わせてapromise満たすために a を使用します。cppreference.comのコード サンプルは、4 つすべてを使用する例を示しています。future

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

版画

待っている...完了!

結果: 7 8 9

3 つのスレッドすべてで Future を使用して結果を取得promiseし、3 番目のスレッドで a を使用してfuture、戻り値以外の方法で a を実現します。また、1 つのスレッドがfutures を介して異なる値で複数の s を満たすpromiseことができますが、それ以外の方法では実行できません。

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

于 2013-01-11T17:48:59.123 に答える
2

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();
}
于 2013-01-11T17:57:11.090 に答える