約束と未来

std::promise と std::future を使用すると、タスクを完全に制御できます。

タスクの完全な制御

std::promise が許可する

  • 値、通知、または例外を設定します。さらに、その結​​果は、promise によって提供される遅延する可能性があります。

std::future は

を許可します
  • Promise から値を取得する
  • Promise に値が利用可能かどうかを尋ねます。
  • 約束の通知を待ちます。その待機は、相対的な期間または絶対的な時点で行うことができます。 => 条件変数の置き換え。
  • 共有の未来 (std::shared_future) を作成します。

両方の通信エンドポイントの約束と未来は、別のスレッドで移動できます。したがって、通信はスレッド間で行われます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// promiseFuture.cpp

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

void product(std::promise<int>&& intPromise, int a, int b){
 intPromise.set_value(a*b);
}

struct Div{

 void operator() (std::promise<int>&& intPromise, int a, int b) const {
 intPromise.set_value(a/b);
 }

};

int main(){

 int a= 20;
 int b= 10;

 std::cout << std::endl;

 // define the promises
 std::promise<int> prodPromise;
 std::promise<int> divPromise;

 // get the futures
 std::future<int> prodResult= prodPromise.get_future();
 std::future<int> divResult= divPromise.get_future();

 // calculate the result in a separat thread
 std::thread prodThread(product,std::move(prodPromise),a,b);
 Div div;
 std::thread divThread(div,std::move(divPromise),a,b);

 // get the result
 std::cout << "20*10= " << prodResult.get() << std::endl;
 std::cout << "20/10= " << divResult.get() << std::endl;

 prodThread.join();
 
 divThread.join();

 std::cout << std::endl;

}

スレッド prodThread (36 行目) は、関数 product (8 行目から 10 行目)、prodPromise (32 行目)、および数値 a と b を使用します。スレッド prodThread の引数を理解するには、関数のシグネチャを確認する必要があります。 prodThread は、最初の引数として callable を必要とします。これは前述の機能製品です。 product には、kind rvalue 参照 (std::promise&&intPromise) の promise と 2 つの数値が必要です。これらは、まさにスレッド prodThread の最後の 3 つの引数です。 36 行目の std::move は右辺値参照を作成します。残りは簡単です。スレッド divThread (38 行目) は、2 つの数値 a と b を除算します。ジョブの場合、クラス Div のインスタンス div を使用します (12 ~ 18 行目)。 div は関数オブジェクトです。

futures は、prodResult.get() および divResult.get() の呼び出しによって結果を取得します。

デフォルトでは、promise と future の間には 1 対 1 の関係があります。ただし、std::shared_future は、promise と多くの先物の間の 1 対多の関係をサポートします。

std::shared_future

std::shared_future

  • 関連する他の先物とは関係なく、promise を要求することができます。
  • std::future と同じインターフェースを持っています。
  • std::future fut で fut.share() を呼び出して作成できます。
  • std::shared_future divResult=divPromise.get_future(). を呼び出す std::promise divPromise によって作成できます。

std::shared_future の管理は特別です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
// sharedFuture.cpp

#include <exception>
#include <future>
#include <iostream>
#include <thread>
#include <utility>

std::mutex coutMutex;

struct Div{

 void operator()(std::promise<int>&& intPromise, int a, int b){
 try{
 if ( b==0 ) throw std::runtime_error("illegal division by zero");
 intPromise.set_value(a/b);
 }
 catch (...){
 intPromise.set_exception(std::current_exception());
 }
 }

};

struct Requestor{

 void operator ()(std::shared_future<int> shaFut){

 // lock std::cout
 std::lock_guard<std::mutex> coutGuard(coutMutex);

 // get the thread id
 std::cout << "threadId(" << std::this_thread::get_id() << "): " ;

 // get the result
 try{
 std::cout << "20/10= " << shaFut.get() << std::endl;
 }
 catch (std::runtime_error& e){
 std::cout << e.what() << std::endl;
 }
 }

};

int main(){

 std::cout << std::endl;

 // define the promises
 std::promise<int> divPromise;

 // get the futures
 std::shared_future<int> divResult= divPromise.get_future();

 // calculate the result in a separat thread
 Div div;
 std::thread divThread(div,std::move(divPromise),20,10);

 Requestor req;
 std::thread sharedThread1(req,divResult);
 std::thread sharedThread2(req,divResult);
 std::thread sharedThread3(req,divResult);
 std::thread sharedThread4(req,divResult);
 std::thread sharedThread5(req,divResult);

 divThread.join();

 sharedThread1.join();
 sharedThread2.join();
 sharedThread3.join();
 sharedThread4.join();
 sharedThread5.join();

 std::cout << std::endl;

}

promise と future の作業パッケージの両方が、この現在の関数オブジェクトの例に含まれています。数を割る場合は、分母に注意する必要があります。 0 であってはなりません。0 の場合は、例外が発生します。 promise は、例外 (18 行目から 20 行目) をキャッチし、それを未来に再スローすることによって、この問題に対処します。 std::future は例外をキャッチし、40 行目に表示します。58 行目では、divPromise が移動され、divThread で実行されます。したがって、std::shared_future はコピーされます 5つのスレッドで。このことをもう一度強調します。移動のみが可能な std::future オブジェクトとは対照的に、std::shared_future オブジェクトをコピーできます。

メイン スレッドは 69 行目から 73 行目で子を待機し、結果を表示します。

次は?

std::async には、知っておくべき奇妙な点が 1 つあります。 by std::async は、関連付けられた promise が完了するまで、デストラクタで将来のブロックを作成しました。奇妙?次の投稿を読んでください。