ロック
ロックは、接尾辞 _lock を持つオブジェクトで、ロックの破棄時に、事前に与えた mutex を unlock します。これは、例外安全なふるまいを実現します。
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 は、ロックの取得を試み、可能なら取得する構文です。