名前空間
変種
操作

std::scoped_lock

From cppreference.com
< cpp‎ | thread
 
 
並行性サポートライブラリ
スレッド
(C++11)
(C++20)
this_thread 名前空間
(C++11)
(C++11)
(C++11)
協調的なキャンセル
排他制御
(C++11)
汎用ロック管理
(C++11)
(C++11)
scoped_lock
(C++17)
(C++11)
(C++11)
(C++11)
条件変数
(C++11)
セマフォ
ラッチとバリア
(C++20)
(C++20)
future
(C++11)
(C++11)
(C++11)
(C++11)
安全なメモリ解放 (Safe Reclamation)
(C++26)
ハザードポインタ
アトミック型
(C++11)
(C++20)
アトミック型の初期化
(C++11)(C++20で非推奨)
(C++11)(C++20で非推奨)
メモリオーダー
(C++11)(C++26で非推奨)
アトミック操作のためのフリー関数
アトミックフラグのためのフリー関数
 
 
ヘッダ <mutex> で定義
template< class... MutexTypes >
class scoped_lock;
(C++17以降)

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

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

scoped_lockクラスはコピーできません。

目次

[編集] テンプレートパラメータ

MutexTypes - ロックするミューテックスの型。型はLockable要件を満たす必要があります。sizeof...(MutexTypes) == 1の場合を除き、その場合、唯一の型はBasicLockableを満たす必要があります。

[編集] メンバ型

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

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

[編集] メンバ関数

scoped_lockを構築し、オプションで指定されたミューテックスをロックする
(public メンバ関数) [編集]
scoped_lockオブジェクトを破棄し、基になるミューテックスをアンロックする
(public メンバ関数) [編集]
operator=
[削除]
コピー代入不可
(public メンバ関数) [編集]

[編集] 注釈

初心者がよく犯す間違いは、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 <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 = "Employee " + id + " has lunch partners: ";
        for (int count{}; const auto& partner : lunch_partners)
            ret += (count++ ? ", " : "") + partner;
        return ret;
    }
};
 
void send_mail(Employee&, Employee&)
{
    // Simulate a time-consuming messaging operation
    std::this_thread::sleep_for(1s);
}
 
void assign_lunch_partner(Employee& e1, Employee& e2)
{
    static std::mutex io_mutex;
    {
        std::lock_guard<std::mutex> lk(io_mutex);
        std::cout << e1.id << " and " << e2.id << " are waiting for locks" << std::endl;
    }
 
    {
        // Use std::scoped_lock to acquire two locks without worrying about
        // other calls to assign_lunch_partner deadlocking us
        // and it also provides a convenient RAII-style mechanism
 
        std::scoped_lock lock(e1.m, e2.m);
 
        // Equivalent code 1 (using std::lock and 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);
 
        // Equivalent code 2 (if unique_locks are needed, e.g. for condition variables)
        // 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);
        {
            std::lock_guard<std::mutex> lk(io_mutex);
            std::cout << e1.id << " and " << e2.id << " got locks" << std::endl;
        }
        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("Bob"), christina("Christina"), dave("Dave");
 
    // Assign in parallel threads because mailing users about lunch assignments
    // takes a long time
    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::cout << alice.partners() << '\n'  << bob.partners() << '\n'
              << christina.partners() << '\n' << dave.partners() << '\n';
}

実行結果の例

Alice and Bob are waiting for locks
Alice and Bob got locks
Christina and Bob are waiting for locks
Christina and Alice are waiting for locks
Dave and Bob are waiting for locks
Dave and Bob got locks
Christina and Alice got locks
Christina and Bob got locks
Employee Alice has lunch partners: Bob, Christina
Employee Bob has lunch partners: Alice, Dave, Christina
Employee Christina has lunch partners: Alice, Bob
Employee Dave has lunch partners: Bob

[編集] 欠陥報告

以下の動作変更を伴う欠陥報告が、以前に公開されたC++標準に遡って適用されました。

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

[編集] 関連項目

ムーブ可能なミューテックス所有権ラッパーを実装する
(クラステンプレート) [編集]
厳密なスコープベースのミューテックス所有権ラッパーを実装する
(クラステンプレート) [編集]
English 日本語 中文(简体) 中文(繁體)