【C++】std::lock_guardとstd::unique_lockの使い方 | mutexを安全に管理する方法

目次

はじめに

マルチスレッドプログラミングでは、複数のスレッドが同じデータ(共有リソース)に同時にアクセスすると、データが破壊されたり、予期せぬ競合状態が発生したりする問題があります。これを防ぐのが、std::mutex(ミューテックス)を使った排他制御(ロック)です。

しかし、mutex.lock()mutex.unlock() を手動で管理すると、関数が途中で例外を投げたり、returnで早期に抜けたりした場合に unlock() が呼ばれず、デッドロックに陥る危険性があります。

この問題を解決するのが、RAII (Resource Acquisition Is Initialization) というC++の原則に基づいた、std::lock_guardstd::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_guardstd::unique_lock
基本機能スコープベースの自動ロック/アンロックスコープベースの自動ロック/アンロック
シンプルさ (非常にシンプル)
オーバーヘッド (ほぼゼロ)△ (少し大きい)
手動ロック/アンロック不可可能
ロック試行 (try_lock)不可可能
所有権の移動不可可能

結論として、単純な排他制御で十分な場合は、より軽量で安全な std::lock_guard を使い、ロック試行や手動アンロックといった、より柔軟な制御が必要な場合にのみ std::unique_lock を使うのがベストプラクティスです。

副業から独立まで「稼げる」Webスキルを習得する(PR)

ここまで読んでいただきありがとうございます。 最後に宣伝をさせてください。

「副業を始めたいが、何から手をつければいいかわからない」「独学でスキルはついたが、収益化できていない」という悩みを持つ方には、マンツーマン指導のWebスクール**「メイカラ」**が適しています。

このスクールは、単に技術を教えるだけでなく、**「副業として具体的にどう稼ぐか」**という実務直結のノウハウ提供に特化している点が特徴です。

講師陣は、実際に「副業Webライターから1年で独立して月収100万円」を達成したプロや、現役で利益を出し続けているブロガーなど、確かな実績を持つプレイヤーのみで構成されています。そのため、机上の空論ではない、現場で通用する戦術を学ぶことができます。

副業に特化した強み

  • 最短ルートの提示: 未経験からでも実績を出せるよう、マンツーマンで指導。
  • AI活用の習得: 副業の時間対効果を最大化するための、正しいAI活用スキルも網羅。
  • 案件獲得のチャンス: 運営がWebマーケティング会社であるため、実力次第で社内案件の紹介など、仕事に直結する可能性があります。

受講者の多くは、「在宅でできる仕事を探している」「副業を頑張りたい」という20代・30代・40代が中心です。

受講前には、講師による無料説明が行われます。無理な勧誘はなく、自分に合った副業スタイルやプランを相談できるため、まずは話を聞いてみることから始めてみてはいかがでしょうか。

ブログで稼ぎたいなら「メイカラ」

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次