std :: promiseとは何ですか?


384

私はC ++ 11にかなり精通していますstd::threadstd::asyncstd::futureの構成要素(例えば参照この回答)ストレートフォワードです。

でも、なんだかよくわからない std::promise、何をする、どのような状況で最適に使用されるは。標準のドキュメント自体には、クラスの概要以外の多くの情報は含まれていません。また、単に:: threadも含まれていません。

誰かstd::promiseが必要であり、それが最も慣用的な解決策である状況の簡潔で簡潔な例を誰かに教えてもらえますか?


2
これを組み込ん
chris

58
本当に、本当に短いバージョンは次のとおり std::promiseですstd::future約束std::futureれた値を取得することができます。get()future を呼び出すと、std::promiseset_valuepromiseを呼び出すことによって)値を設定するの所有者まで待機します。値が設定される前にプロミスが破棄され、get()そのプロミスに関連付けられているフューチャーを呼び出すと、値がプロミスstd::broken_promiseされていたために例外が発生しますが、それを取得することは不可能です。
James McNellis

14
私はあなたが/したいことができれば、それをお勧めします、見とる行動でC ++同時実行をすることによってアンソニー・ウィリアムズ
デビッド・ロドリゲス- dribeas

32
@KerrekSB std::broken_promiseは、標準ライブラリで最高の名前付き識別子です。そして、ありませんstd::atomic_future
Cubbi

3
反対意見、あなたの反対意見を説明してくれませんか?
Kerrek SB

回答:


182

[futures.state]の言葉でstd::futureは、a は非同期の戻りオブジェクト(「共有状態から結果を読み取るオブジェクト」)であり、a std::promise非同期プロバイダー(「結果を共有状態に提供するオブジェクト」)です。つまり、約束はあなたが結果を設定するものであり、関連する未来からそれを得ることができます。

非同期プロバイダーは、フューチャーが参照する共有状態を最初に作成するものです。std::promiseは非同期プロバイダーの1つのタイプでstd::packaged_taskあり、もう1つのタイプですstd::async。それらのそれぞれは、共有状態を作成し、std::futureその状態を共有するを提供し、状態を準備することができます。

std::asyncは、非同期の結果オブジェクトを提供し、内部で非同期プロバイダーを作成し、タスクの完了時に共有状態を準備する、より高度な便利なユーティリティです。あなたはそれをstd::packaged_task(またはstd::bindand std::promise)とaでエミュレートできますが、std::threadより安全で使いやすいstd::asyncです。

std::promise非同期の結果を将来に渡したいが、結果を準備するコードを、に渡すのに適した単一の関数でラップすることができない場合のために、少し低いレベルですstd::async。たとえば、複数promiseのと関連するの配列があり、複数のfuture計算を実行して各プロミスに結果を設定する単一のスレッドがあるとします。async単一の結果のみを返すことを許可します。複数を返すには、async数回呼び出す必要があり、リソースを浪費する可能性があります。


10
資源を無駄にするかもしれませんか?そのコードを並列化できない場合は、正しくない可能性があります。
パピー

「非同期リターン」と「共有状態からの読み取り結果」はほとんどが直交しているため、最初の文は少しわかりにくくなっています。国家の共有は未来と約束の間にあるということですか?その場合は、その旨を最初から明記してください。
einpoklum

@einpoklumなぜ最後の単語の前に「非同期戻りオブジェクト」を読むのをやめたのですか?標準の用語を引用しています。A future非同期戻りオブジェクトの具体例です。これは、共有状態を介して非同期で返された結果を読み取るオブジェクトです。A promise非同期プロバイダーの具体例です。非同期プロバイダーは、値を共有状態に書き込み、非同期で読み取ることができるオブジェクトです。私が書いたものを意味しました。
ジョナサンウェイクリー

496

私は状況を少しよく理解したので(ここでの回答により少なからず!)、私自身の記事を少し追加すると思いました。


C ++ 11には、関連しますが、2つの異なる概念があります。非同期計算(別の場所で呼び出される関数)と同時実行(スレッド、同時に動作するもの)です。2つはやや直交する概念です。非同期計算は、関数呼び出しの別のフレーバーですが、スレッドは実行コンテキストです。スレッドはそれ自体で有用ですが、この説明では、スレッドを実装の詳細として扱います。


非同期計算には抽象化の階層があります。たとえば、いくつかの引数をとる関数があるとします。

int foo(double, char, bool);

まずstd::future<T>、typeの将来の値を表すテンプレートがありTます。値はメンバー関数を介して取得できます。メンバー関数get()は、結果を待つことでプログラムを効果的に同期させます。または、futureはをサポートしますwait_for()。これは、結果がすでに利用可能かどうかを調べるために使用できます。フューチャーは、通常の戻り値型の非同期ドロップイン置換と考える必要があります。この例の関数では、が必要std::future<int>です。

さて、階層の上位から最下位へ:

  1. std::async:非同期計算を実行する最も便利で簡単な方法は、async関数テンプレートを使用することです。これにより、一致するfutureがすぐに返されます。

    auto fut = std::async(foo, 1.5, 'x', false);  // is a std::future<int>

    詳細はほとんど制御できません。特に、関数が同時に実行されているのか、シリアルで実行さget()れているのか、または他の黒魔術によって実行されているのかもわかりません。ただし、結果は必要なときに簡単に取得できます。

    auto res = fut.get();  // is an int
  2. これで、のようなものを実装する方法を検討できますasyncが、制御する方法で行います。たとえば、関数を別のスレッドで実行するように要求する場合があります。std::threadクラスによって別のスレッドを提供できることはすでに知っています。

    次の下位レベルの抽象化は、まさにそれを行いますstd::packaged_task。これは、関数をラップし、関数の戻り値にフューチャーを提供するテンプレートですが、オブジェクト自体は呼び出し可能であり、呼び出しはユーザーの裁量に任されています。次のように設定できます。

    std::packaged_task<int(double, char, bool)> tsk(foo);
    
    auto fut = tsk.get_future();    // is a std::future<int>

    タスクを呼び出して呼び出しが完了すると、未来は準備が整います。これは、別のスレッドにとって理想的なジョブです。タスクを必ずスレッドに移動する必要があります。

    std::thread thr(std::move(tsk), 1.5, 'x', false);

    スレッドはすぐに実行を開始します。それをするかdetachjoin(例えばアンソニー・ウィリアムズの使用範囲の最後に、またはいつでもscoped_thread本当に標準ライブラリであるべきラッパーを、)。std::threadただし、使用の詳細はここでは関係ありません。thr最終的には必ず参加または分離してください。重要なのは、関数呼び出しが終了するたびに、結果が用意できることです。

    auto res = fut.get();  // as before
  3. これで最低レベルに到達しました。パッケージ化されたタスクをどのように実装しますか これがstd::promise出てくるところです。約束は未来と通信するためのビルディングブロックです。主な手順は次のとおりです。

    • 呼び出しスレッドは約束をします。

    • 呼び出しスレッドは、約束から未来を取得します。

    • promiseは、関数の引数とともに、別のスレッドに移動されます。

    • 新しいスレッドが関数を実行し、約束を満たします。

    • 元のスレッドが結果を取得します。

    例として、ここに私たち自身の「パッケージ化されたタスク」があります:

    template <typename> class my_task;
    
    template <typename R, typename ...Args>
    class my_task<R(Args...)>
    {
        std::function<R(Args...)> fn;
        std::promise<R> pr;             // the promise of the result
    public:
        template <typename ...Ts>
        explicit my_task(Ts &&... ts) : fn(std::forward<Ts>(ts)...) { }
    
        template <typename ...Ts>
        void operator()(Ts &&... ts)
        {
            pr.set_value(fn(std::forward<Ts>(ts)...));  // fulfill the promise
        }
    
        std::future<R> get_future() { return pr.get_future(); }
    
        // disable copy, default move
    };

    このテンプレートの使用法は、基本的にの使用法と同じですstd::packaged_task。タスク全体を移動すると、promiseを移動することになります。さらにアドホックな状況では、promiseオブジェクトを明示的に新しいスレッドに移動して、それをスレッド関数の関数引数にすることもできますが、上記のようなタスクラッパーは、より柔軟で煩わしくないソリューションのようです。


例外を作る

約束は例外と密接に関連しています。promiseのインターフェースだけではその状態を完全に伝えるには不十分であるため、promiseの操作が意味をなさない場合は常に例外がスローされます。すべての例外はstd::future_errorから派生するタイプstd::logic_errorです。まず、いくつかの制約の説明:

  • デフォルトで作成されたプロミスは非アクティブです。非アクティブな約束は結果なしで死ぬ可能性があります。

  • を介して未来が取得されると、約束がアクティブになりget_future()ます。ただし、1つだけ取得できる未来はです。

  • 将来が消費される場合、promiseは、その存続期間が終了する前にviaによって満たされるset_value()か、例外を設定set_exception()する必要があります。満足した約束は結果なしで死ぬことget()があり、将来利用できるようになります。例外付きのpromiseはget()、将来の呼び出し時に保存された例外を発生させます。約束が価値も例外もなく死んだ場合、get()未来を要求すると「破られた約束」の例外が発生します。

以下は、これらのさまざまな例外的な動作を示すための小さなテストシリーズです。まず、ハーネス:

#include <iostream>
#include <future>
#include <exception>
#include <stdexcept>

int test();

int main()
{
    try
    {
        return test();
    }
    catch (std::future_error const & e)
    {
        std::cout << "Future error: " << e.what() << " / " << e.code() << std::endl;
    }
    catch (std::exception const & e)
    {
        std::cout << "Standard exception: " << e.what() << std::endl;
    }
    catch (...)
    {
        std::cout << "Unknown exception." << std::endl;
    }
}

次にテストに進みます。

ケース1:非アクティブな約束

int test()
{
    std::promise<int> pr;
    return 0;
}
// fine, no problems

ケース2:アクティブなプロミス、未使用

int test()
{
    std::promise<int> pr;
    auto fut = pr.get_future();
    return 0;
}
// fine, no problems; fut.get() would block indefinitely

ケース3:未来が多すぎる

int test()
{
    std::promise<int> pr;
    auto fut1 = pr.get_future();
    auto fut2 = pr.get_future();  //   Error: "Future already retrieved"
    return 0;
}

ケース4:満足した約束

int test()
{
    std::promise<int> pr;
    auto fut = pr.get_future();

    {
        std::promise<int> pr2(std::move(pr));
        pr2.set_value(10);
    }

    return fut.get();
}
// Fine, returns "10".

ケース5:満足度が高すぎる

int test()
{
    std::promise<int> pr;
    auto fut = pr.get_future();

    {
        std::promise<int> pr2(std::move(pr));
        pr2.set_value(10);
        pr2.set_value(10);  // Error: "Promise already satisfied"
    }

    return fut.get();
}

以上のものがある場合は、同じ例外がスローされたいずれかset_valueset_exception

ケース6:例外

int test()
{
    std::promise<int> pr;
    auto fut = pr.get_future();

    {
        std::promise<int> pr2(std::move(pr));
        pr2.set_exception(std::make_exception_ptr(std::runtime_error("Booboo")));
    }

    return fut.get();
}
// throws the runtime_error exception

ケース7:約束が破られた

int test()
{
    std::promise<int> pr;
    auto fut = pr.get_future();

    {
        std::promise<int> pr2(std::move(pr));
    }   // Error: "broken promise"

    return fut.get();
}

あなたは「結果を待つことでプログラムを効果的に同期させる」と言った。ここで「同期」とはどういう意味ですか?ステートメント全体はどういう意味ですか?理解できません。このディクショナリエントリの「同期」の意味は、文を理解するのに役立ちません。「待つ」というのは「同期」という意味ですか?すべての待機は同期しますか?私はあなたが何を意味しているのか一部理解していると思いますが、あなたが実際に何を意味しているのかはわかりません。
Nawaz 2013

9
いい答えです、あなたの助けに感謝します。std:: asyncの部分について、私はそれが別のスレッドを生成するか、flag(std :: launch :: async、std :: launch :: deferred)と同期して動作することを決定できたことを覚えています。
StereoMatching 2013年

1
@FelixDombek:完全転送などにstd::functionは多くのコンストラクタがあります。それらをの消費者に公開しない理由はありませんmy_task
Kerrek SB、2014

1
@DaveedV .:フィードバックをありがとう!はい、それはテストケース7です。値も例外も設定せずにpromiseを破棄した場合get()、futureを呼び出すと例外が発生します。「破棄される前に」を追加して、これを明確にします。それが十分に明確であるかどうか私に知らせてください。
Kerrek SB、2015

3
最後にgot()、あなたのすばらしい説明futureのスレッドサポートライブラリをざっと見てみてpromiseください。
晴れた月

33

Bartosz Milewskiが優れた記事を提供しています。

C ++はフューチャーの実装を小さなブロックのセットに分割します

std :: promiseはこれらの部分の1つです。

promiseは、関数を実行しているスレッドから、関数futureを利用するスレッドに戻り値(または例外)を渡すための手段です。

...

futureは、promiseチャネルの受信側の周りに構築された同期オブジェクトです。

したがって、futureを使用したい場合、非同期処理の結果を取得するために使用するという約束に終わります。

このページの例は次のとおりです。

promise<int> intPromise;
future<int> intFuture = intPromise.get_future();
std::thread t(asyncFun, std::move(intPromise));
// do some other stuff
int result = intFuture.get(); // may throw MyException

4
スレッドのコンストラクタでの約束を見て、ついにペニーが落ちました。Bartoszの記事は多分最高ではないかもしれませんが、要素がどのように結びついているかを説明しています。ありがとう。
Kerrek SB

28

おおまかな近似ではstd::promise、のもう一方の端と見なすことができますstd::future(これはfalseですが、説明のために、あたかもそうであるかのように考えることができます)。通信チャネルのコンシューマ側はa std::futureを使用して共有状態からデータを消費し、プロデューサスレッドはa std::promiseを使用して共有状態に書き込みます。


12
@KerrekSB:std::async概念的には(これは規格では義務付けられていません)を作成しstd::promise、それを(ソートの、スレッドプールである可能性がある、新しいスレッドである可能性がある)スレッドプールにプッシュして返す関数として理解できます。std::future呼び出し元に関連付けられた。クライアント側では、で待機し、反対側のstd::futureスレッドで結果を計算してに格納しstd::promiseます。注:標準が必要と共有状態std::futureではなく、の存在std::promise、この特定のユースケースでは。
デビッドロドリゲス-dribeas

6
@KerrekSB:スレッドでstd::futureは呼び出さjoinれません。実際の通信バッファーである共有状態へのポインターがあります。共有状態は、同期メカニズム(おそらく持っているstd::function+ std::condition_variableまで呼び出し側をロックするstd::promise満たされているが。スレッドの実行がすべてこれに直交しており、多くの実装では、あなたがその見つけるかもしれないstd::asyncその後、接合された新しいスレッドで実行されるのではなく、むしろ、その寿命プログラムの終わりまで拡張し、スレッドプールによって。
デビッド・ロドリゲス- dribeas

1
@DavidRodríguez-dribeas:コメントからの情報を回答に編集してください。
マルク・ムッツ-mmutz

2
@JonathanWakely:新しいスレッドで実行する必要があるという意味ではなく、新しく作成されたスレッドで実行されたかのように非同期に実行する必要があるだけです。の主な利点std::asyncは、作成するスレッドの数に関してランタイムライブラリが適切な判断を下せることです。ほとんどの場合、スレッドプールを使用するランタイムが期待されます。現在、VS2012は内部でスレッドプールを使用しており、as-ifルールに違反していません。この特定のas-ifに対して満たす必要がある保証はほとんどないことに注意してください。
デビッドロドリゲス-dribeas

1
スレッドローカルは再初期化する必要がありますが、as-ifルールは何でも許可します(そのため、私はイタリック体で「まるで」のように表記します:)
Jonathan Wakely

11

std::promise非同期関数から返される情報のチャネルまたは経路です。std::futureは、同期メカニズムであり、呼び出し元を、で運ばれる戻り値のstd::promise準備ができるまで待機します(つまり、その値は関数内に設定されます)。


8

非同期処理には、実際には3つのコアエンティティがあります。C ++ 11は現在、そのうちの2つに焦点を当てています。

ロジックを非同期で実行するために必要な中心的なものは次のとおりです。

  1. タスク(ロジックは、いくつかの関手オブジェクトとしてパッケージ)「どこか」を実行します。
  2. 実際の処理ノード -スレッド、プロセス、等彼らは、それに提供される場合、このようなファンクタを実行します。基本的なワーカースレッドプールがこれをどのように行うかについての良いアイデアについては、「コマンド」設計パターンを見てください。
  3. 結果ハンドル:誰かがその結果を必要とし、彼らのためにそれを取得するオブジェクトを必要とします。OOPおよびその他の理由により、待機または同期はこのハンドルのAPIで行う必要があります。

C ++ 11は、私が(1)std::promiseで話したこと、および(3)で話したことを呼び出しますstd::futurestd::thread(2)に対して公的に提供される唯一のものです。実際のプログラムはスレッドとメモリのリソースを管理する必要があるため、これは残念です。ほとんどの場合、小さなタスクごとにスレッドを作成して破棄する代わりに、スレッドプールでタスクを実行する必要があります(ほとんどの場合、それ自体で不要なパフォーマンスヒットを引き起こし、リソースを簡単に作成できます。さらに悪い飢餓)。

Herb Sutter氏やその他のC ++ 11ブレイントラストによるとstd::executor、Javaの場合と同様に、スレッドプールの基礎となる論理的に類似したセットアップを(2)に追加するという暫定的な計画があります。おそらくC ++ 2014で見られるかもしれませんが、私の賭けはC ++ 17に似ています(そして、彼らがこれらの標準を台無しにした場合、神は私たちを助けてくれます)。


7

A std::promiseはpromise / futureペアのエンドポイントとして作成され、std::futureget_future()メソッドを使用してstd :: promiseから作成された)がもう一方のエンドポイントです。これは、1つのスレッドがメッセージを介して別のスレッドにデータを提供するときに、2つのスレッドが同期する方法を提供する単純なワンショット方式です。

1つのスレッドがデータを提供するプロミスを作成し、他のスレッドが将来プロミスを収集すると考えることができます。このメカニズムは1回しか使用できません。

promise / futureメカニズムは、を使用するスレッドからの一方向のみです set_value()方法をstd::promise使用してスレッドへget()ののstd::futureデータを受信します。get()future のメソッドが複数回呼び出されると、例外が生成されます。

とのスレッドをstd::promise使用していない場合set_value()第二のスレッド呼び出したときに、その約束を履行するためget()のは、std::future約束を収集するために約束を持つ最初のスレッドによって満たされるまで、2番目のスレッドが待機状態になりますstd::promiseが使用するときset_value()の方法をデータを送信します。

提案コルーチンと技術仕様N4663は、言語プログラミング-コルーチンのためのC ++の拡張とのVisual Studioの2017 C ++コンパイラのサポートをco_await、使用することも可能であるstd::futureし、std::asyncコルーチン機能作成ます。https://stackoverflow.com/a/50753040/1466970の説明と例を参照してください。これには、std::futurewith の使用について説明するセクションが1つありますco_await

次のサンプルコードは、シンプルなVisual Studio 2013 Windowsコンソールアプリケーションで、C ++ 11同時実行クラス/テンプレートおよびその他の機能のいくつかを使用する方法を示しています。これは、正常に機能するpromise / future、いくつかのタスクを実行して停止する自律スレッド、およびより多くの同期動作が必要であり、複数の通知が必要なためにpromise / futureペアが機能しない場合の使用を示しています。

この例に関する1つの注意点は、さまざまな場所で追加される遅延です。これらの遅延は、使用してコンソールに出力されるさまざまなメッセージを確認するためにのみ追加されましたstd::coutが明確になり、複数のスレッドからのテキストが混ざらないよう。

の最初の部分は、main()3つの追加スレッドを作成し、std::promisestd::future、スレッド間でデータを送信して送信します。興味深い点は、メインスレッドがスレッドT2を起動するところです。スレッドT2はメインスレッドからのデータを待機し、何かを実行してから、3番目のスレッドT3にデータを送信します。メインスレッド。

の2番目の部分では、main()2つのスレッドと一連のキューを作成して、メインスレッドから作成された2つのスレッドそれぞれに複数のメッセージを送信できるようにします。私たちは、使用することはできませんstd::promiseし、std::future約束/将来のデュオはワンショットであり、繰り返し使用することはできませんので、このために。

クラスのソースSync_queueは、StroustrupのThe C ++ Programming Language:4th Editionからのものです。

// cpp_threads.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <thread>  // std::thread is defined here
#include <future>  // std::future and std::promise defined here

#include <list>    // std::list which we use to build a message queue on.

static std::atomic<int> kount(1);       // this variable is used to provide an identifier for each thread started.

//------------------------------------------------
// create a simple queue to let us send notifications to some of our threads.
// a future and promise are one shot type of notifications.
// we use Sync_queue<> to have a queue between a producer thread and a consumer thread.
// this code taken from chapter 42 section 42.3.4
//   The C++ Programming Language, 4th Edition by Bjarne Stroustrup
//   copyright 2014 by Pearson Education, Inc.
template<typename Ttype>
class Sync_queue {
public:
    void  put(const Ttype &val);
    void  get(Ttype &val);

private:
    std::mutex mtx;                   // mutex used to synchronize queue access
    std::condition_variable cond;     // used for notifications when things are added to queue
    std::list <Ttype> q;              // list that is used as a message queue
};

template<typename Ttype>
void Sync_queue<Ttype>::put(const Ttype &val) {
    std::lock_guard <std::mutex> lck(mtx);
    q.push_back(val);
    cond.notify_one();
}

template<typename Ttype>
void Sync_queue<Ttype>::get(Ttype &val) {
    std::unique_lock<std::mutex> lck(mtx);
    cond.wait(lck, [this]{return  !q.empty(); });
    val = q.front();
    q.pop_front();
}
//------------------------------------------------


// thread function that starts up and gets its identifier and then
// waits for a promise to be filled by some other thread.
void func(std::promise<int> &jj) {
    int myId = std::atomic_fetch_add(&kount, 1);   // get my identifier
    std::future<int> intFuture(jj.get_future());
    auto ll = intFuture.get();   // wait for the promise attached to the future
    std::cout << "  func " << myId << " future " << ll << std::endl;
}

// function takes a promise from one thread and creates a value to provide as a promise to another thread.
void func2(std::promise<int> &jj, std::promise<int>&pp) {
    int myId = std::atomic_fetch_add(&kount, 1);   // get my identifier
    std::future<int> intFuture(jj.get_future());
    auto ll = intFuture.get();     // wait for the promise attached to the future

    auto promiseValue = ll * 100;   // create the value to provide as promised to the next thread in the chain
    pp.set_value(promiseValue);
    std::cout << "  func2 " << myId << " promised " << promiseValue << " ll was " << ll << std::endl;
}

// thread function that starts up and waits for a series of notifications for work to do.
void func3(Sync_queue<int> &q, int iBegin, int iEnd, int *pInts) {
    int myId = std::atomic_fetch_add(&kount, 1);

    int ll;
    q.get(ll);    // wait on a notification and when we get it, processes it.
    while (ll > 0) {
        std::cout << "  func3 " << myId << " start loop base " << ll << " " << iBegin << " to " << iEnd << std::endl;
        for (int i = iBegin; i < iEnd; i++) {
            pInts[i] = ll + i;
        }
        q.get(ll);  // we finished this job so now wait for the next one.
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    std::chrono::milliseconds myDur(1000);

    // create our various promise and future objects which we are going to use to synchronise our threads
    // create our three threads which are going to do some simple things.
    std::cout << "MAIN #1 - create our threads." << std::endl;

    // thread T1 is going to wait on a promised int
    std::promise<int> intPromiseT1;
    std::thread t1(func, std::ref(intPromiseT1));

    // thread T2 is going to wait on a promised int and then provide a promised int to thread T3
    std::promise<int> intPromiseT2;
    std::promise<int> intPromiseT3;

    std::thread t2(func2, std::ref(intPromiseT2), std::ref(intPromiseT3));

    // thread T3 is going to wait on a promised int and then provide a promised int to thread Main
    std::promise<int> intPromiseMain;
    std::thread t3(func2, std::ref(intPromiseT3), std::ref(intPromiseMain));

    std::this_thread::sleep_for(myDur);
    std::cout << "MAIN #2 - provide the value for promise #1" << std::endl;
    intPromiseT1.set_value(22);

    std::this_thread::sleep_for(myDur);
    std::cout << "MAIN #2.2 - provide the value for promise #2" << std::endl;
    std::this_thread::sleep_for(myDur);
    intPromiseT2.set_value(1001);
    std::this_thread::sleep_for(myDur);
    std::cout << "MAIN #2.4 - set_value 1001 completed." << std::endl;

    std::future<int> intFutureMain(intPromiseMain.get_future());
    auto t3Promised = intFutureMain.get();
    std::cout << "MAIN #2.3 - intFutureMain.get() from T3. " << t3Promised << std::endl;

    t1.join();
    t2.join();
    t3.join();

    int iArray[100];

    Sync_queue<int> q1;    // notification queue for messages to thread t11
    Sync_queue<int> q2;    // notification queue for messages to thread t12

    std::thread t11(func3, std::ref(q1), 0, 5, iArray);     // start thread t11 with its queue and section of the array
    std::this_thread::sleep_for(myDur);
    std::thread t12(func3, std::ref(q2), 10, 15, iArray);   // start thread t12 with its queue and section of the array
    std::this_thread::sleep_for(myDur);

    // send a series of jobs to our threads by sending notification to each thread's queue.
    for (int i = 0; i < 5; i++) {
        std::cout << "MAIN #11 Loop to do array " << i << std::endl;
        std::this_thread::sleep_for(myDur);  // sleep a moment for I/O to complete
        q1.put(i + 100);
        std::this_thread::sleep_for(myDur);  // sleep a moment for I/O to complete
        q2.put(i + 1000);
        std::this_thread::sleep_for(myDur);  // sleep a moment for I/O to complete
    }

    // close down the job threads so that we can quit.
    q1.put(-1);    // indicate we are done with agreed upon out of range data value
    q2.put(-1);    // indicate we are done with agreed upon out of range data value

    t11.join();
    t12.join();
    return 0;
}

この単純なアプリケーションは、次の出力を作成します。

MAIN #1 - create our threads.
MAIN #2 - provide the value for promise #1
  func 1 future 22
MAIN #2.2 - provide the value for promise #2
  func2 2 promised 100100 ll was 1001
  func2 3 promised 10010000 ll was 100100
MAIN #2.4 - set_value 1001 completed.
MAIN #2.3 - intFutureMain.get() from T3. 10010000
MAIN #11 Loop to do array 0
  func3 4 start loop base 100 0 to 5
  func3 5 start loop base 1000 10 to 15
MAIN #11 Loop to do array 1
  func3 4 start loop base 101 0 to 5
  func3 5 start loop base 1001 10 to 15
MAIN #11 Loop to do array 2
  func3 4 start loop base 102 0 to 5
  func3 5 start loop base 1002 10 to 15
MAIN #11 Loop to do array 3
  func3 4 start loop base 103 0 to 5
  func3 5 start loop base 1003 10 to 15
MAIN #11 Loop to do array 4
  func3 4 start loop base 104 0 to 5
  func3 5 start loop base 1004 10 to 15

1

約束はワイヤーのもう一方の端です。

futureによって計算されているの値を取得する必要があると想像してくださいasync。しかし、あなたはそれが同じスレッドで計算したくない、あなたも「今」スレッドを生成していない-あなたは知らないので、多分あなたのソフトウェアは、プールからスレッドを選択するために設計されていたでしょう最後にche計算を実行します。

さて、この(まだ不明)スレッド/クラス/エンティティに何を渡しますか?futureこれは結果なので、を渡しません。に接続されていて、ワイヤのもう一方の端futureを表すものを渡したいので、実際に何かを計算/書き込む人についての知識がなくても単にクエリを実行します。future

これがpromiseです。それはあなたに接続されたハンドルfutureです。場合future、スピーカー、およびでget()いくつかの音が出てくるまでは待機を開始、promiseであるマイク。しかし、それはただのマイクではありませんあなたが保持するスピーカーに単一のワイヤで接続したマイク。あなたは相手が誰であるか知っているかもしれませんが、あなたはそれを知る必要はありません-あなたはそれを与えて、相手が何かを言うまで待つだけです。


0

http://www.cplusplus.com/reference/future/promise/

1つの文の説明:furture :: get()はpromse :: set_value()を永久に待機します。

void print_int(std::future<int>& fut) {
    int x = fut.get(); // future would wait prom.set_value forever
    std::cout << "value: " << x << '\n';
}

int main()
{
    std::promise<int> prom;                      // create promise

    std::future<int> fut = prom.get_future();    // engagement with future

    std::thread th1(print_int, std::ref(fut));  // send future to new thread

    prom.set_value(10);                         // fulfill promise
                                                 // (synchronizes with getting the future)
    th1.join();
    return 0;
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.