はじめに
マルチスレッドプログラミングでは、複数のスレッドが同じデータ(共有リソース)に同時にアクセスすると、データが破壊されたり、予期せぬ競合状態が発生したりする問題があります。これを防ぐのが、std::mutex(ミューテックス)を使った排他制御(ロック)です。
しかし、mutex.lock() と mutex.unlock() を手動で管理すると、関数が途中で例外を投げたり、returnで早期に抜けたりした場合に unlock() が呼ばれず、デッドロックに陥る危険性があります。
この問題を解決するのが、RAII (Resource Acquisition Is Initialization) というC++の原則に基づいた、std::lock_guard と std::unique_lock という2つのクラスです。これらのクラスは、オブジェクトが生成されたときにロックを取得し、スコープを抜けるときに自動的にロックを解放してくれます。
1. std::lock_guard: シンプルで確実なロック管理
std::lock_guard は、最もシンプルでオーバーヘッドの少ないロック管理クラスです。スコープベースで、基本的な排他ロック機能を提供します。
特徴
- コンストラクタで
mutexをロックする。 - デストラクタ(スコープを抜けるとき)で
mutexを自動的にアンロックする。 - 途中で手動アンロックしたり、ロックの所有権を移動させたりはできない。
サンプルコード
#include <iostream>
#include <thread>
#include <mutex>
#include <vector>
// 共有リソース(この例では標準出力)を保護するためのミューテックス
std::mutex mtx;
void print_message(const std::string& message) {
// lock_guardオブジェクトが生成されると同時に、mtxがロックされる
std::lock_guard<std::mutex> guard(mtx);
// このブロック内は、一度に一つのスレッドしか実行できない
std::cout << message << " : 開始" << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 何か重い処理をシミュレート
std::cout << message << " : 終了" << std::endl;
// この関数の } を抜けるとき、guardのデストラクタが呼ばれ、mtxが自動的にアンロックされる
}
int main() {
std::vector<std::thread> threads;
for (int i = 0; i < 5; ++i) {
threads.emplace_back(print_message, "スレッド " + std::to_string(i));
}
for (auto& th : threads) {
th.join();
}
return 0;
}
解説: print_message 関数では、lock_guard がスコープを抜ける際に確実に unlock を呼んでくれるため、lock/unlock の呼び忘れを心配する必要が全くありません。
2. std::unique_lock: より柔軟なロック管理
std::unique_lock は、lock_guard の機能に加え、より高度で柔軟なロック管理の仕組みを提供します。
特徴
lock_guardと同様の、スコープベースの自動ロック/アンロック。- 手動でのロック/アンロック (
.lock(),.unlock()) が可能。 - ロックの試行 (
.try_lock()) が可能。 - ロックの所有権を移動(ムーブ)できる。
サンプルコード
#include <iostream>
#include <thread>
#include <mutex>
std::mutex resource_mutex;
void process_data(int id) {
// 1. ロックせずにunique_lockオブジェクトを作成 (defer_lock)
std::unique_lock<std::mutex> lock(resource_mutex, std::defer_lock);
std::cout << "スレッド " << id << " はロックを試みます..." << std::endl;
// 2. ロックを試行
if (lock.try_lock()) {
std::cout << "スレッド " << id << " はロックに成功しました。" << std::endl;
// ... 共有リソースを使った処理 ...
std::this_thread::sleep_for(std::chrono::milliseconds(200));
// 3. 手動でアンロック(早く解放したい場合)
lock.unlock();
std::cout << "スレッド " << id << " はロックを解放しました。" << std::endl;
} else {
std::cout << "スレッド " << id << " はロックに失敗しました。" << std::endl;
}
}
int main() {
std::thread t1(process_data, 1);
std::thread t2(process_data, 2);
t1.join();
t2.join();
return 0;
}
解説: unique_lock は、try_lock(もしロックできたら true、できなかったら false を返す)のような、より複雑なロック戦略を実装する際に強力なツールとなります。
まとめ
| 機能 | std::lock_guard | std::unique_lock |
| 基本機能 | スコープベースの自動ロック/アンロック | スコープベースの自動ロック/アンロック |
| シンプルさ | ◎ (非常にシンプル) | ◯ |
| オーバーヘッド | ◎ (ほぼゼロ) | △ (少し大きい) |
| 手動ロック/アンロック | 不可 | 可能 |
ロック試行 (try_lock) | 不可 | 可能 |
| 所有権の移動 | 不可 | 可能 |
結論として、単純な排他制御で十分な場合は、より軽量で安全な std::lock_guard を使い、ロック試行や手動アンロックといった、より柔軟な制御が必要な場合にのみ std::unique_lock を使うのがベストプラクティスです。
副業から独立まで「稼げる」Webスキルを習得する(PR)
ここまで読んでいただきありがとうございます。 最後に宣伝をさせてください。
「副業を始めたいが、何から手をつければいいかわからない」「独学でスキルはついたが、収益化できていない」という悩みを持つ方には、マンツーマン指導のWebスクール**「メイカラ」**が適しています。
このスクールは、単に技術を教えるだけでなく、**「副業として具体的にどう稼ぐか」**という実務直結のノウハウ提供に特化している点が特徴です。
講師陣は、実際に「副業Webライターから1年で独立して月収100万円」を達成したプロや、現役で利益を出し続けているブロガーなど、確かな実績を持つプレイヤーのみで構成されています。そのため、机上の空論ではない、現場で通用する戦術を学ぶことができます。
副業に特化した強み
- 最短ルートの提示: 未経験からでも実績を出せるよう、マンツーマンで指導。
- AI活用の習得: 副業の時間対効果を最大化するための、正しいAI活用スキルも網羅。
- 案件獲得のチャンス: 運営がWebマーケティング会社であるため、実力次第で社内案件の紹介など、仕事に直結する可能性があります。
受講者の多くは、「在宅でできる仕事を探している」「副業を頑張りたい」という20代・30代・40代が中心です。
受講前には、講師による無料説明が行われます。無理な勧誘はなく、自分に合った副業スタイルやプランを相談できるため、まずは話を聞いてみることから始めてみてはいかがでしょうか。
