C++ 標準ライブラリ<mutex>

マルチスレッドプログラミングにおいて、データの一貫性とスレッドセーフティを確保することが極めて重要です。

C++ 標準ライブラリは、共有リソースへのアクセスを制御するための豊富な同期プリミティブを提供します。

C++ 標準ライブラリの<mutex>ヘッダーファイルは、マルチスレッドプログラムでスレッド間の同期と相互排他を実現するための一連のツールを提供します。

<mutex>ヘッダーファイルはC++11で導入されたもので、ミューテックス(mutex)用のクラスと関数が含まれています。ミューテックスは、複数のスレッドが同時に共有リソースにアクセスするのを防ぐための同期メカニズムです。

ミューテックス(Mutex)は、共有リソースへのアクセスを制御するための同期プリミティブです。スレッドが共有リソースにアクセスする必要がある場合、ミューテックスのロックを試みます。ミューテックスがすでに他のスレッドによってロックされている場合、要求したスレッドはミューテックスが解放されるまでブロックされます。

基本構文

C++ では、<mutex>ヘッダーファイルは以下の主要なクラスを提供します:

  • std::mutex:基本的なミューテックス。
  • std::recursive_mutex:再帰ミューテックス。同じスレッドが複数回ロックすることを許可します。
  • std::timed_mutex:タイムアウト機能を備えたミューテックス。
  • std::recursive_timed_mutex:タイムアウト機能を備えた再帰ミューテックス。

例

1. 使用std::mutex

以下は、C++ でstd::mutexを使用して共有リソースへのアクセスを同期する方法を示す簡単な例です。

例

#include <iostream>
#include <thread>
#include <mutex>

std::mutex mtx; // 全局互斥锁
int shared_resource = 0;

void increment() {
    for (int i = 0; i < 10000; ++i) {
        mtx.lock(); // 锁定互斥锁
        ++shared_resource;
        mtx.unlock(); // 解锁互斥锁
    }
}

int main() {
    std::thread t1(increment);
    std::thread t2(increment);

    t1.join();
    t2.join();

    std::cout << "Final value of shared_resource: " << shared_resource << std::endl;
    return 0;
}

出力結果:

Final value of shared_resource: 20000

2. 使用std::recursive_mutex

再帰ミューテックスは、同じスレッドが同じミューテックスを複数回ロックすることを許可します。以下は、std::recursive_mutexを使用した例です。

例

#include <iostream>
#include <thread>
#include <mutex>

std::recursive_mutex rmtx; // 再帰 mutex オブジェクトを作成する
int shared_resource = 0; // 共享资源

// 递归函数
void recursive_increment(int count) {
    if (count <= 0) return;

    std::lock_guard<std::recursive_mutex> lock(rmtx); // 上锁,确保线程安全
    ++shared_resource;
    std::cout << "Incremented shared_resource to " << shared_resource << " (count = " << count << ")" << std::endl;

    // 递归调用
    recursive_increment(count - 1);
}

int main() {
    std::thread t1(recursive_increment, 3); // 线程 t1 执行 recursive_increment(3)
    std::thread t2(recursive_increment, 3); // 线程 t2 执行 recursive_increment(3)
   
    t1.join(); // 等待线程 t1 完成
    t2.join(); // 等待线程 t2 完成

    std::cout << "Final value of shared_resource: " << shared_resource << std::endl;

    return 0;
}

出力結果:

Incremented shared_resource to 1 (count = 3)
Incremented shared_resource to 2 (count = 2)
Incremented shared_resource to 3 (count = 1)
Incremented shared_resource to 4 (count = 0)
Incremented shared_resource to 5 (count = 3)
Incremented shared_resource to 6 (count = 2)
Incremented shared_resource to 7 (count = 1)
Incremented shared_resource to 8 (count = 0)
Final value of shared_resource: 8

コード解説:

  1. 作成std::recursive_mutexオブジェクト:std::recursive_mutex rmtx;は再帰ミューテックスであり、同じスレッドが複数回ロックを取得できるようにします。

  2. 共有リソース:int shared_resource = 0;複数のスレッドが共同でアクセスするリソースです。

  3. 再帰関数recursive_increment:

    • 使用std::lock_guard<std::recursive_mutex>に対してrmtxロックします。再帰ミューテックスを使用しているため、同じスレッドが複数回ロックを取得できます。
    • によって++shared_resource共有リソースの値を増加させます。
    • 関数自体が再帰呼び出しを行い、再帰関数が複数回ロックする状況で安全に動作する方法を示しています。
  4. スレッドの作成と実行:

    • std::thread t1(recursive_increment, 3);とstd::thread t2(recursive_increment, 3);2つのスレッドをそれぞれ作成し、両方とも を実行します。recursive_increment(3)。
    • t1.join()とt2.join()2つのスレッドの実行完了を待ちます。

注意事項

  • デッドロックを避けるため、ミューテックスをロックするたびに必ずロック解除を行うようにしてください。
  • 使用std::lock_guard 或 std::unique_lockを使用すると、ミューテックスのロックとロック解除を自動的に管理でき、エラーの可能性を減らせます。
  • 例外をスローする可能性のある関数をミューテックスを保持したまま呼び出さないでください。デッドロックを引き起こす可能性があります。

<mutex>はC++標準ライブラリにおいて非常に重要なヘッダーファイルであり、マルチスレッドプログラミングに基本的な同期メカニズムを提供します。ミューテックスを使用することで、共有リソースへのアクセスが安全であることを保証し、データ競合や不整合の問題を回避できます。


詳細説明

1. std::mutex

基本的なミューテックスを提供し、同時に1つのスレッドだけが共有リソースにアクセスできるようにします。

例

#include <mutex>

std::mutex mtx;

void thread_function() {
    std::lock_guard<std::mutex> lock(mtx);
    // 访问共享资源
}

2. std::recursive_mutex

同じスレッドが複数回ロックを取得してもデッドロックを引き起こさず、再帰関数に特に役立ちます。

例

#include <mutex>

std::recursive_mutex rmtx;

void recursive_function(int count) {
    if (count <= 0) return;
    std::lock_guard<std::recursive_mutex> lock(rmtx);
    // 递归调用
    recursive_function(count - 1);
}

3. std::timed_mutex

タイマー付きミューテックスを提供し、ロック取得を試みる際にタイムアウト時間を設定できます。

例

#include <mutex>
#include <chrono>

std::timed_mutex tm;

void try_lock_for_example() {
    if (tm.try_lock_for(std::chrono::seconds(1))) {
        // 成功获得锁
        tm.unlock();
    } else {
        // 锁获取失败
    }
}

4. std::recursive_timed_mutex

std::timed_mutex から継承し、同じスレッドが複数回ロックを取得でき、タイマー機能もサポートします。

例

#include <mutex>
#include <chrono>

std::recursive_timed_mutex rtm;

void recursive_timed_function(int count) {
    if (count <= 0) return;
    if (rtm.try_lock_for(std::chrono::seconds(1))) {
        // 成功获得锁
        recursive_timed_function(count - 1);
        rtm.unlock();
    } else {
        // 锁获取失败
    }
}

5. std::lock_guard

std::mutex のロックを自動管理するラッパーであり、RAII スタイルを使用して、スコープ終了時に自動的にロックが解放されることを保証します。

例

#include <mutex>

std::mutex mtx;

void function() {
    std::lock_guard<std::mutex> lock(mtx);
    // 访问共享资源
}

6. std::unique_lock

std::lock_guard より柔軟なロック管理を提供し、手動でのロック解放と再取得が可能で、タイマーロックにも対応しています。

例

#include <mutex>
#include <chrono>

std::mutex mtx;

void function() {
    std::unique_lock<std::mutex> lock(mtx);
    // 访问共享资源
   
    // 可以手动释放锁
    lock.unlock();
   
    // 可以重新获得锁
    lock.lock();
   
    // 可以进行定时锁定
    if (lock.try_lock_for(std::chrono::seconds(1))) {
        // 成功获得锁
    }
}

7. std::adopt_lock_t

フラグ型であり、std::unique_lock が既存のロックを採用することを指定します。

例

std::mutex mtx;
std::unique_lock<std::mutex> lock(mtx, std::adopt_lock);

8. std::defer_lock_t

機能:フラグ型であり、遅延ロック用で、初期化時にはロックしません。

例

std::mutex mtx;
std::unique_lock<std::mutex> lock(mtx, std::defer_lock);
// 後で lock.lock() を呼び出すことができます

9. std::try_to_lock_t

フラグ型であり、ブロックせずにロックを試みるために使用します。

例

std::mutex mtx1, mtx2;
std::unique_lock<std::mutex> lock1(mtx1, std::try_to_lock);
std::unique_lock<std::mutex> lock2(mtx2, std::try_to_lock);

if (lock1 && lock2) {
    // 成功获得两个锁
}
その他の拡張