スレッド同期アクション
11280 ワード
スレッド同期、すなわち、1つのスレッドが完了する前に、特定のことが発生するのを待つか、条件の達成を待つ必要があります.スレッド同期の動作は、条件変数(condition_variable)および所望の(future)で実現できる.
condition_variable
condition_variableヘッダファイルには、待機(wait)および通知(notify)シリーズに使用できる関数が含まれています.
待機関数
説明する
wait()
通知が起動するまでスレッドをブロック
wait_for()
タイムアウト待ちまたは通知による起動までスレッドをブロック
wait_until()
スレッドを一時的に停止したり、通知によって起動されたりします.
通知関数
説明する
notify_one()
現在待機している同じconditionを起動します.variableオブジェクトのスレッド
notify_all()
現在待機しているすべての同じconditionを起動します.variableオブジェクトのスレッド
例
コードでprint_id関数では,まずreadyに鍵をかけ,readyの値を判断する.readyがtrueでない場合、条件変数cvを使用してスレッドをブロックし、反発量をロックして他のスレッドにアクセスできるようにします.go()readyを変更した後、条件変数cvのnotify_を呼び出すall()は、cvブロックのすべてのスレッドに通知し、それらが起動され、信号量に対する競合状態に再進入し、反発ロックを獲得し、以下の動作を実行する.特筆すべきは、notify()シリーズ関数は同じcondition_に対してのみ使用されます.variableオブジェクトwait()のスレッドが通知操作を行う.次のようになります.
上記コードではfoo()がcv 1でブロックされ、barがcv 2でブロックされ、cv_notify()は、cv 1によってブロックされたスレッドを起動します.ではfoo()は呼び覚まされ、bar()は別のcv 2の使用を待つしかない.notify_one()のスレッドが起動します.
future
futureヘッダファイルには、非同期操作を許可する一連の関数とクラスが含まれています.
async-非同期呼び出し関数
非同期呼び出し関数asyncはfnを呼び出し、futureオブジェクトがfnを持つ戻り結果を返し、戻り時にfnの実行完了を待たない.追加のパラメータも入力できます.このパラメータタイプはstd::launchで、fnがいつ呼び出されるかを制御します.
パラメータ
説明
launch::async
関数に独立したスレッドを作成し、すぐに実行します.
launch::deferred
関数の呼び出しがwait()またはget()関数呼び出しに遅延した場合に実行されます.
launch::deferred | launch::async
どちらでも構いません(デフォルト)
例:
メイン関数では、1 async関数を呼び出してスレッド実行is_を作成します.prime()関数は、パラメータとして21を入力し、boolに特化したfutureオブジェクトを返します.その後、他の独自の操作を実行します.関数is_を取得する必要がある場合primeの戻り結果の場合、②はfuture 1のget()で戻り値を取得しretに与える.
packaged_task<>——タスク
packaged_taskクラスは関数をパッケージし、その結果を非同期でチェックし、get_を介してfutureオブジェクトに保存できます.future()を取得します.packaged_task<>のテンプレートパラメータは、fooパッケージのときにpackaged_を構築する関数署名を試みます.task<>オブジェクトは
packaged_taskで包装されたタスクは2つの方法で実行できます:1.packaged_の使用taskリロードoperator()パラメータ呼び出し2.threadオブジェクトを作成し、タスクとパラメータを呼び出します.
例:
メイン関数の最初のブロックコードはpackaged_を通過します.task<>のoperator()は、タスクパッケージの関数を呼び出し、このスレッドで実行します.複数のパラメータを受信し、パッケージされた関数に渡し、返されたfutureインスタンスを使用して関数の実行結果を取得できます.第2のブロックコードは、スレッドを作成することによってパッケージの関数を呼び出し、他のスレッドで実行し、futureによって結果を得る.
promise-承諾
Promiseオブジェクトは、あるタイプのTの値を保存することができます.この値はfutureオブジェクトで読み取ることができます(別のスレッドにおいても可能である)ため、promiseはスレッドを同期させる手段も提供する.get_futureにより、promiseオブジェクトに関連付けられたfutureオブジェクトを取得し、関数を呼び出した後、2つのオブジェクトは同じ共有状態を共有する.set_valueにより、関連付けられたpromiseオブジェクトとfutureオブジェクトとの間の共有値を設定することができる.promisの場合eオブジェクトが設定されていない場合はfutureを呼び出す.get()ではスレッドがブロックされます.set_value()の設定が完了し、対応するfutureがブロック状態から準備状態に変わり、格納された値を取得できます.例:
condition_variable
condition_variableヘッダファイルには、待機(wait)および通知(notify)シリーズに使用できる関数が含まれています.
待機関数
説明する
wait()
通知が起動するまでスレッドをブロック
wait_for()
タイムアウト待ちまたは通知による起動までスレッドをブロック
wait_until()
スレッドを一時的に停止したり、通知によって起動されたりします.
通知関数
説明する
notify_one()
現在待機している同じconditionを起動します.variableオブジェクトのスレッド
notify_all()
現在待機しているすべての同じconditionを起動します.variableオブジェクトのスレッド
例
#include // std::cout
#include // std::thread
#include // std::mutex, std::unique_lock
#include // std::condition_variable
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void print_id (int id) {
std::unique_lock<std::mutex> lck(mtx);
while (!ready) cv.wait(lck);
// ...
std::cout << "thread " << id << '
';
}
void go() {
std::unique_lock<std::mutex> lck(mtx);
ready = true;
cv.notify_all();
}
int main ()
{
std::thread threads[10];
// spawn 10 threads:
for (int i=0; i<10; ++i)
threads[i] = std::thread(print_id,i);
std::cout << "10 threads ready to race...
";
go(); // go!
for (auto& th : threads) th.join();
return 0;
}
コードでprint_id関数では,まずreadyに鍵をかけ,readyの値を判断する.readyがtrueでない場合、条件変数cvを使用してスレッドをブロックし、反発量をロックして他のスレッドにアクセスできるようにします.go()readyを変更した後、条件変数cvのnotify_を呼び出すall()は、cvブロックのすべてのスレッドに通知し、それらが起動され、信号量に対する競合状態に再進入し、反発ロックを獲得し、以下の動作を実行する.特筆すべきは、notify()シリーズ関数は同じcondition_に対してのみ使用されます.variableオブジェクトwait()のスレッドが通知操作を行う.次のようになります.
mutex m;
condition_variable cv1,cv2;
void foo(){
...
lock_gound lck(m);
cv1.wait(lck);
...
}
void bar(){
...
lock_gound lck(m);
cv2.wait(lck);
...
}
void cv_notify(){
...
cv1.notify_one();
...
}
int main(){
thread t1(foo);
thread t2(bar);
thread t3(cv_notify);
t1.join();
t2.join();
t3.join();
return 0;
}
上記コードではfoo()がcv 1でブロックされ、barがcv 2でブロックされ、cv_notify()は、cv 1によってブロックされたスレッドを起動します.ではfoo()は呼び覚まされ、bar()は別のcv 2の使用を待つしかない.notify_one()のスレッドが起動します.
future
futureヘッダファイルには、非同期操作を許可する一連の関数とクラスが含まれています.
async-非同期呼び出し関数
非同期呼び出し関数asyncはfnを呼び出し、futureオブジェクトがfnを持つ戻り結果を返し、戻り時にfnの実行完了を待たない.追加のパラメータも入力できます.このパラメータタイプはstd::launchで、fnがいつ呼び出されるかを制御します.
パラメータ
説明
launch::async
関数に独立したスレッドを作成し、すぐに実行します.
launch::deferred
関数の呼び出しがwait()またはget()関数呼び出しに遅延した場合に実行されます.
launch::deferred | launch::async
どちらでも構いません(デフォルト)
例:
#include
#include
using namespace std;
bool is_prime(int x){
for(int i=2;iif(x%i==0){
return false;
}
}
return true;
}
int main(){
future<bool> future1 = async(is_prime,21); // ①
...
bool ret =future1.get(); // ②
if(ret){
cout << "21 is prime";
}else{
cout << "21 is not prime";
}
return 0;
}
メイン関数では、1 async関数を呼び出してスレッド実行is_を作成します.prime()関数は、パラメータとして21を入力し、boolに特化したfutureオブジェクトを返します.その後、他の独自の操作を実行します.関数is_を取得する必要がある場合primeの戻り結果の場合、②はfuture 1のget()で戻り値を取得しretに与える.
packaged_task<>——タスク
packaged_taskクラスは関数をパッケージし、その結果を非同期でチェックし、get_を介してfutureオブジェクトに保存できます.future()を取得します.packaged_task<>のテンプレートパラメータは、fooパッケージのときにpackaged_を構築する関数署名を試みます.task<>オブジェクトは
int foo(double, std::string&);
に特化する必要があります.このように構成されたオブジェクトptはget_を呼び出すfuture()の場合に返されるfutureオブジェクトはpackaged_task pt(foo);
型であるべきである.packaged_taskで包装されたタスクは2つの方法で実行できます:1.packaged_の使用taskリロードoperator()パラメータ呼び出し2.threadオブジェクトを作成し、タスクとパラメータを呼び出します.
例:
#include
#include
using namespace std;
bool is_prime(int x) {
for (int i = 2; i < x; i++) {
if (x % i == 0) {
return false;
}
}
return true;
}
int main() {
int x1 = 41, x2 = 42;
// operator() pt1
packaged_task<bool(int)> pt1(is_prime);
pt1(x1);
auto ret1 = pt1.get_future().get();
if (ret1) {
cout << x1 << " is prime" << endl;
} else {
cout << x1 << " is not prime" << endl;
}
// pt2
packaged_task<bool(int)> pt2(is_prime);
future<bool> f = pt2.get_future();
thread t(move(pt2), ref(x2));
auto ret = f.get();
if (ret) {
cout << x2 << " is prime" << endl;
} else {
cout << x2 << " is not prime" << endl;
}
t.join();
return 0;
}
メイン関数の最初のブロックコードはpackaged_を通過します.task<>のoperator()は、タスクパッケージの関数を呼び出し、このスレッドで実行します.複数のパラメータを受信し、パッケージされた関数に渡し、返されたfutureインスタンスを使用して関数の実行結果を取得できます.第2のブロックコードは、スレッドを作成することによってパッケージの関数を呼び出し、他のスレッドで実行し、futureによって結果を得る.
promise-承諾
Promiseオブジェクトは、あるタイプのTの値を保存することができます.この値はfutureオブジェクトで読み取ることができます(別のスレッドにおいても可能である)ため、promiseはスレッドを同期させる手段も提供する.get_futureにより、promiseオブジェクトに関連付けられたfutureオブジェクトを取得し、関数を呼び出した後、2つのオブジェクトは同じ共有状態を共有する.set_valueにより、関連付けられたpromiseオブジェクトとfutureオブジェクトとの間の共有値を設定することができる.promisの場合eオブジェクトが設定されていない場合はfutureを呼び出す.get()ではスレッドがブロックされます.set_value()の設定が完了し、対応するfutureがブロック状態から準備状態に変わり、格納された値を取得できます.例:
#include
#include
#include
using namespace std;
void print_future(future<int>& f){
cout << "inside future"<cout << "get "<// 。
}
int main() {
promise<int> prom; // promise
future<int> fut = prom.get_future(); // future
thread t(print_future,ref(fut)); // future
this_thread::sleep_for(chrono::seconds(2)); // , 2
prom.set_value(20); //
t.join();
return 0;
}