ロック
ロックは、接尾辞 _lock を持つオブジェクトで、ロックの破棄時に、事前に与えた mutexunlock します。これは、例外安全なふるまいを実現します。
std::mutex m; int n; // 共有データ void f() { std::unique_lock<mutex> l(m); // ここで lock している n++; // 共有データの操作 } // ここで unlock している
複数のロックを取得する際は、以下のコードのようにすべきです。
std::mutex m1, m2, m3; void f() { std::unique_lock<mutex> l1(m1, std::defer_lock); std::unique_lock<mutex> l2(m2, std::defer_lock); std::unique_lock<mutex> l3(m3, std::defer_lock); lock(l1, l2, l3); } // ここで unlock している
defer_lock ポリシーは、渡した mutex をロックせずに unique_lock の管理下へ置きます。その後、前コードのように、破棄前にどこかで lock しなければなりません。
std::mutex m1, m2, m3; void f() { lock(l1, l2, l3); std::unique_lock<mutex> l1(m1, std::adopt_lock); std::unique_lock<mutex> l2(m2, std::adopt_lock); std::unique_lock<mutex> l3(m3, std::adopt_lock); } // ここで unlock している
adopt_lock ポリシーでは、ロック済みの mutex を渡し、それを unique_lock の管理下へ置きます。
以下のコードは、std::mutex を使った排他処理のサンプルです。
// timelock.cpp #include <chrono> #include <iostream> #include <mutex> #include <thread> using namespace std; mutex m; void locker() { // 定期的にロックをかけたりはずしたり for(int i = 0; i < 3; i++) { { lock_guard<mutex> l(m); cout << "locking with second" << endl; this_thread::sleep_for(chrono::seconds(1)); cout << "releasing with second" << endl; } this_thread::sleep_for(chrono::seconds(1)); } } void acquisition() { // 定期的に locker() のロックを取得しに行く for(int i = 0; i < 10; i++) { { unique_lock<mutex> l(m, try_to_lock); if(l) { cout << "lock successful" << endl; cout << "releasing successful" << endl; } else { cout << "lock failed, hibernating" << endl; } } this_thread::sleep_for(chrono::milliseconds(500)); } } int main(int, char *[]) { thread t(locker); this_thread::sleep_for(chrono::milliseconds(500)); acquisition(); t.join(); return 0; }
locker() は一秒おきに mutex のロック/非ロックを繰り返します。lock_guard は、実体化したスコープにロックをかける構文です。
acquisition() は 0.5秒おきにロックを取得しようと試みます。try_to_lock ポリシーを設定した unique_lock は、ロックの取得を試み、可能なら取得する構文です。