Namespaces
Variants

std:: scoped_lock

From cppreference.net
Concurrency support library
Threads
(C++11)
(C++20)
this_thread namespace
(C++11)
(C++11)
Cooperative cancellation
Mutual exclusion
Generic lock management
(C++11)
(C++11)
scoped_lock
(C++17)
(C++11)
(C++11)
Condition variables
(C++11)
Semaphores
Latches and Barriers
(C++20)
(C++20)
Futures
(C++11)
(C++11)
(C++11)
Safe reclamation
Hazard pointers
Atomic types
(C++11)
(C++20)
Initialization of atomic types
(C++11) (deprecated in C++20)
(C++11) (deprecated in C++20)
Memory ordering
(C++11) (deprecated in C++26)
Free functions for atomic operations
Free functions for atomic flags
ヘッダーで定義 <mutex>
template < class ... MutexTypes >
class scoped_lock ;
(C++17以降)

scoped_lock クラスは、スコープブロックの期間中に0個以上のミューテックスを所有するための便利な RAIIスタイル のメカニズムを提供するミューテックスラッパーです。

scoped_lock オブジェクトが作成されると、与えられたミューテックスの所有権の取得を試みます。制御が scoped_lock オブジェクトが作成されたスコープから離れると、 scoped_lock は破棄され、ミューテックスが解放されます。複数のミューテックスが与えられた場合、デッドロック回避アルゴリズムが std::lock によって使用されるかのように適用されます。

scoped_lock クラスはコピー不可です。

目次

テンプレートパラメータ

MutexTypes - ロックするミューテックスの型。型は Lockable 要件を満たさなければならない。ただし sizeof... ( MutexTypes ) == 1 の場合、唯一の型は BasicLockable を満たさなければならない

メンバー型

メンバー型 定義
mutex_type
(条件付きで存在)

sizeof... ( MutexTypes ) == 1 の場合、メンバー型 mutex_type MutexTypes... 内の唯一の型である Mutex と同じです。 それ以外の場合、メンバー mutex_type は存在しません。

メンバー関数

scoped_lock を構築し、オプションで指定されたミューテックスをロックする
(public member function)
scoped_lock オブジェクトを破棄し、基盤となるミューテックスのロックを解除する
(public member function)
operator=
[deleted]
コピー代入不可
(public member function)

注記

初心者がよく犯すエラーは、 scoped_lock 変数に名前を「付け忘れる」ことです。例えば、 std :: scoped_lock ( mtx ) ; (これは mtx という名前の scoped_lock 変数をデフォルト構築する)や、 std :: scoped_lock { mtx } ; (これは即座に破棄されるprvalueオブジェクトを構築する)などです。これにより、スコープの残りの期間ミューテックスを保持するロックが実際には構築されません。

機能テスト マクロ 標準 機能
__cpp_lib_scoped_lock 201703L (C++17) std::scoped_lock

以下の例では、デッドロックなしにミューテックスのペアをロックするために std::scoped_lock を使用しており、RAIIスタイルです。

#include <chrono>
#include <functional>
#include <iostream>
#include <mutex>
#include <string>
#include <syncstream>
#include <thread>
#include <vector>
using namespace std::chrono_literals;
struct Employee
{
    std::vector<std::string> lunch_partners;
    std::string id;
    std::mutex m;
    Employee(std::string id) : id(id) {}
    std::string partners() const
    {
        std::string ret = "従業員 " + id + " はランチパートナーを持っています: ";
        for (int count{}; const auto& partner : lunch_partners)
            ret += (count++ ? ", " : "") + partner;
        return ret;
    }
};
void send_mail(Employee&, Employee&)
{
    // 時間のかかるメッセージング操作をシミュレート
    std::this_thread::sleep_for
(注:元のテキストはC++の標準ライブラリ関数名であり、HTMLタグや属性も含まれていないため、翻訳対象となる自然言語のテキストが存在しません。C++の専門用語は翻訳しないという指示に従い、そのまま出力しています)(1s);
}
void assign_lunch_partner(Employee& e1, Employee& e2)
{
    std::osyncstream
(注:指定された条件により、HTMLタグ・属性は翻訳せず、タグ内のテキストも翻訳対象外のため、元の形式を保持したまま出力しています) synced_out(std::cout);
    synced_out << e1.id << " および " << e2.id << " はロックを待機中" << std::endl
(注:元のテキストはC++の標準ライブラリ要素を指しており、翻訳対象外のため、HTMLタグを保持したまま出力しています);
    {
        // std::scoped_lockを使用して、二つのロックを取得し、デッドロックの心配をせずに
        // assign_lunch_partnerへの他の呼び出しがデッドロックを引き起こしている
        // また、便利なRAIIスタイルのメカニズムも提供します
        std::scoped_lock lock(e1.m, e2.m);
        // 同等のコード 1 (std::lock と std::lock_guard を使用)
        // std::lock(e1.m, e2.m);
        // std::lock_guard<std::mutex> lk1(e1.m, std::adopt_lock);
        // std::lock_guard<std::mutex> lk2(e2.m, std::adopt_lock);
        // 同等のコード 2 (unique_lockが必要な場合、例: 条件変数用)
        // std::unique_lock<std::mutex> lk1(e1.m, std::defer_lock);
        // std::unique_lock<std::mutex> lk2(e2.m, std::defer_lock);
        // std::lock(lk1, lk2);
        synced_out << e1.id << " および " << e2.id << " ロックを取得しました" << std::endl
(注:元のテキスト「std::endl」はC++固有の用語であり、HTMLタグ内に含まれているため、翻訳対象外としました。表示上は変更ありませんが、必要に応じて説明文を追加しています);
        e1.lunch_partners.push_back(e2.id);
        e2.lunch_partners.push_back(e1.id);
    }
    send_mail(e1, e2);
    send_mail(e2, e1);
}
int main()
{
    Employee alice("Alice"), bob("ボブ"), christina("クリスティーナ"), dave("Dave");
    // ランチ割り当てについてユーザーにメール送信するため、並列スレッドで割り当てる
    // 長時間を要する
    std::vector<std::thread> threads;
    threads.emplace_back(assign_lunch_partner, std::ref(alice), std::ref(bob));
    threads.emplace_back(assign_lunch_partner, std::ref(christina), std::ref(bob));
    threads.emplace_back(assign_lunch_partner, std::ref(christina), std::ref(alice));
    threads.emplace_back(assign_lunch_partner, std::ref(dave), std::ref(bob));
    for (auto& thread : threads)
        thread.join();
    std::osyncstream
(注:指定された条件により、HTMLタグ・属性は翻訳せず、C++固有の用語も翻訳していません。表示されるテキスト「std::osyncstream」はC++標準ライブラリのクラス名であるため、原文のまま保持しています)(std::cout) << alice.partners() << '\n'  
                                << bob.partners() << '\n'
                                << christina.partners() << '\n' 
                                << dave.partners() << '\n';
}

出力例:

アリスとボブはロックを待っています
アリスとボブはロックを取得しました
クリスティーナとボブはロックを待っています
クリスティーナとアリスはロックを待っています
デイブとボブはロックを待っています
デイブとボブはロックを取得しました
クリスティーナとアリスはロックを取得しました
クリスティーナとボブはロックを取得しました
従業員アリスのランチパートナー: ボブ、クリスティーナ
従業員ボブのランチパートナー: アリス、デイブ、クリスティーナ
従業員クリスティーナのランチパートナー: アリス、ボブ
従業員デイブのランチパートナー: ボブ

不具合報告

以下の動作変更の欠陥報告書は、以前に公開されたC++規格に対して遡及的に適用されました。

DR 適用対象 公開時の動作 正しい動作
LWG 2981 C++17 scoped_lock<MutexTypes...> からの冗長なdeduction guideが提供されていた 削除

関連項目

移動可能なミューテックス所有権ラッパーを実装する
(クラステンプレート)
(C++11)
厳密なスコープベースのミューテックス所有権ラッパーを実装する
(クラステンプレート)