読者です 読者をやめる 読者になる 読者になる

スコープロックでの4つのロック方法

C++

ミューテックスを自動的にアンロックするためのstd::lock_guardクラスとstd::unique_lockクラスでは、そのコンストラクタでいくつかのロック方法を提供しています。


1. 何も指定しない
まず、何も指定しない場合。この場合、ミューテックスクラスのlock()メンバ関数を使用してロックの取得が行われます。これはstd::lock_guardとstd::unique_lockの両方でサポートされています。

#include <iostream>
#include <mutex>

int main()
{
    std::mutex mtx;
    {
        std::unique_lock<std::mutex> lk(mtx); // lock()が呼ばれる

        // 共有リソースにアクセス...

    } // unlock()が呼ばれる
}


2. defer_lock
std::defer_lockを指定した場合、すぐにはロックを取得せず、あとから自分でlock()、try_lock()、try_lock_for()、try_lock_until()を好きに呼び出せます。これはstd::unique_lockでのみサポートされています。
コンストラクタの呼び出し時点では、ロック取得中かどうかを示すowns_lock()メンバ関数の結果はfalseとなります。

#include <iostream>
#include <mutex>

int main()
{
    std::mutex mtx;
    {
        std::unique_lock<std::mutex> lk(mtx, std::defer_lock); // lock()が呼ばれない

        mtx.lock(); // ロックを取得する

        // 共有リソースにアクセス...

    } // unlock()が呼ばれる
}

3. try_to_lock
std::try_to_lockを指定した場合、コンストラクタ内ではlock()の代わりにtry_lock()が実行されます。ロックの取得に失敗した場合はowns_lock() == falseになるので、状況によってエラー報告やロック取得のリトライをします。これはstd::unique_lockでのみサポートされます。

#include <iostream>
#include <mutex>
#include <stdexcept>

int main()
{
    std::mutex mtx;
    {
        std::unique_lock<std::mutex> lk(mtx, std::try_to_lock); // try_lock()が呼ばれる

        if (!lk.owns_lock()) {
            throw std::runtime_error("ロックの取得に失敗!");
        }

        // 共有リソースにアクセス...

    } // unlock()が呼ばれる
}

4. adopt_lock
std::adopt_lockを指定した場合、そのコンストラクタは「ロック済みミューテックス」を受け取り、ロック取得のための関数は呼ばれません。std::defer_lockとの違いは、std::defer_lockが「ロック取得していない(owns_lock() == false)」状態になるのに対し、std::adopt_lockは「ロック取得している(owns_lock() == true)」になる、ということです。これはstd::lock_guardとstd::unique_lockの両方でサポートされます。

#include <iostream>
#include <mutex>

int main()
{
    std::mutex mtx;
    {
        mtx.lock(); // 事前にロックを取得する
        std::unique_lock<std::mutex> lk(mtx, std::adopt_lock); // コンストラクタではロック取得しない

        // 共有リソースにアクセス...

    } // unlock()が呼ばれる
}


参照:
std::defer_lock - cpprefjp
std::try_to_lock - cpprefjp
std::adopt_lock - cpprefjp