Namespaces
Variants
Views
Actions

Difference between revisions of "cpp/thread/lock guard"

From cppreference.com
< cpp‎ | thread
m (Text replace - "{{cpp|" to "{{c|")
(Notes: add the other case from scoped_lock)
 
(44 intermediate revisions by 19 users not shown)
Line 1: Line 1:
 
{{cpp/title|lock_guard}}
 
{{cpp/title|lock_guard}}
{{cpp/thread/lock_guard/sidebar}}
+
{{cpp/thread/lock_guard/navbar}}
{{ddcl | header=mutex | notes={{mark since c++11}} | 1=
+
{{ddcl|header=mutex|since=c++11|1=
 
template< class Mutex >
 
template< class Mutex >
 
class lock_guard;
 
class lock_guard;
 
}}
 
}}
  
The class {{c|lock_guard}} implements a strictly scope-based mutex ownership wrapper. The class is non-copyable.
+
The class {{tt|lock_guard}} is a mutex wrapper that provides a convenient [[cpp/language/raii|RAII-style]] mechanism for owning a mutex for the duration of a scoped block.
The supplied Mutex type shall implement the Lockable concept.
+
  
{{todo}}
+
When a {{tt|lock_guard}} object is created, it attempts to take ownership of the mutex it is given. When control leaves the scope in which the {{tt|lock_guard}} object was created, the {{tt|lock_guard}} is destructed and the mutex is released.
 +
 
 +
The {{tt|lock_guard}} class is non-copyable.
 +
 
 +
===Template parameters===
 +
{{par begin}}
 +
{{par|Mutex|the type of the mutex to lock. The type must meet the {{named req|BasicLockable}} requirements}}
 +
{{par end}}
  
 
===Member types===
 
===Member types===
{{tdcl list begin}}
+
{{dsc begin}}
{{tdcl list hitem | Member type | Definition}}
+
{{dsc hitem|Member type|Definition}}
{{tdcl list item | {{tt|mutex_type}} | Mutex}}
+
{{dsc|{{tt|mutex_type}}|Mutex}}
{{tdcl list end}}
+
{{dsc end}}
  
 
===Member functions===
 
===Member functions===
{{dcl list begin}}
+
{{dsc begin}}
{{dcl list template | cpp/thread/lock_guard/dcl list constructor | Locks the supplied mutex}}
+
{{dsc inc|cpp/thread/lock_guard/dsc constructor}}
{{dcl list template | cpp/thread/lock_guard/dcl list destructor | Unlocks the supplied mutex}}
+
{{dsc inc|cpp/thread/lock_guard/dsc destructor}}
{{tdcl list end}}
+
{{dsc inc|cpp/thread/lock_guard/dsc operator{{=}}}}
 +
{{dsc end}}
 +
 
 +
===Notes===
 +
A common beginner error is to "forget" to give a {{tt|lock_guard}} variable a name, e.g. {{c|std::lock_guard(mtx);}} (which default constructs a {{tt|lock_guard}} variable named {{tt|mtx}}) or {{c|std::lock_guard{mtx};}} (which constructs a prvalue object that is immediately destroyed), thereby not actually constructing a lock that holds a mutex for the rest of the scope.
 +
 
 +
{{rrev|since=c++17|
 +
{{ltt|cpp/thread/scoped_lock|std::scoped_lock}} offers an alternative for {{tt|lock_guard}} that provides the ability to lock multiple mutexes using a deadlock avoidance algorithm.
 +
}}
  
 
===Example===
 
===Example===
{{example|
+
{{example
| code=std::mutex m;
+
|Demonstrates safe and unsafe increments of a volatile variable by two threads.
 +
|code=
 +
#include <iostream>
 +
#include <mutex>
 +
#include <string_view>
 +
#include <syncstream>
 +
#include <thread>
  
void do_something()
+
volatile int g_i = 0;
 +
std::mutex g_i_mutex;  // protects g_i
 +
 
 +
void safe_increment(int iterations)
 
{
 
{
     std::lock_guard<std::mutex> lock(m);
+
     const std::lock_guard<std::mutex> lock(g_i_mutex);
     // the remaining function is thread-safe now
+
    while (iterations-- > 0)
 +
        g_i = g_i + 1;
 +
    std::cout << "thread #" << std::this_thread::get_id() << ", g_i: " << g_i << '\n';
 +
 
 +
     // g_i_mutex is automatically released when lock goes out of scope
 
}
 
}
| output=
+
 
 +
void unsafe_increment(int iterations)
 +
{
 +
    while (iterations-- > 0)
 +
        g_i = g_i + 1;
 +
    std::osyncstream(std::cout) << "thread #" << std::this_thread::get_id()
 +
                                << ", g_i: " << g_i << '\n';
 +
}
 +
 
 +
int main()
 +
{
 +
    auto test = [](std::string_view fun_name, auto fun)
 +
    {
 +
        g_i = 0;
 +
        std::cout << fun_name << ":\nbefore, g_i: " << g_i << '\n';
 +
        {
 +
            std::jthread t1(fun, 1'000'000);
 +
            std::jthread t2(fun, 1'000'000);
 +
        }
 +
        std::cout << "after, g_i: " << g_i << "\n\n";
 +
    };
 +
    test("safe_increment", safe_increment);
 +
    test("unsafe_increment", unsafe_increment);
 +
}
 +
|p=true
 +
|output=
 +
safe_increment:
 +
before, g_i: 0
 +
thread #140121493231360, g_i: 1000000
 +
thread #140121484838656, g_i: 2000000
 +
after, g_i: 2000000
 +
 
 +
unsafe_increment:
 +
before, g_i: 0
 +
thread #140121484838656, g_i: 1028945
 +
thread #140121493231360, g_i: 1034337
 +
after, g_i: 1034337
 
}}
 
}}
 +
 +
===Defect reports===
 +
{{dr list begin}}
 +
{{dr list item|wg=lwg|dr=2981|std=C++17|before=redundant deduction guide from {{tt|lock_guard<Mutex>}} was provided|after=removed}}
 +
{{dr list end}}
 +
 +
===See also===
 +
{{dsc begin}}
 +
{{dsc inc|cpp/thread/dsc unique_lock}}
 +
{{dsc inc|cpp/thread/dsc scoped_lock}}
 +
{{dsc end}}
 +
 +
{{langlinks|de|es|fr|it|ja|pt|ru|zh}}

Latest revision as of 07:00, 6 July 2023

 
 
Concurrency support library
Threads
(C++11)
(C++20)
this_thread namespace
(C++11)
(C++11)
(C++11)
Cooperative cancellation
Mutual exclusion
(C++11)
Generic lock management
(C++11)
lock_guard
(C++11)
(C++11)
(C++11)
(C++11)
Condition variables
(C++11)
Semaphores
Latches and Barriers
(C++20)
(C++20)
Futures
(C++11)
(C++11)
(C++11)
(C++11)
Safe Reclamation
(C++26)
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
Free functions for atomic operations
Free functions for atomic flags
 
std::lock_guard
 
Defined in header <mutex>
template< class Mutex >
class lock_guard;
(since C++11)

The class lock_guard is a mutex wrapper that provides a convenient RAII-style mechanism for owning a mutex for the duration of a scoped block.

When a lock_guard object is created, it attempts to take ownership of the mutex it is given. When control leaves the scope in which the lock_guard object was created, the lock_guard is destructed and the mutex is released.

The lock_guard class is non-copyable.

Contents

[edit] Template parameters

Mutex - the type of the mutex to lock. The type must meet the BasicLockable requirements

[edit] Member types

Member type Definition
mutex_type Mutex

[edit] Member functions

constructs a lock_guard, optionally locking the given mutex
(public member function) [edit]
destructs the lock_guard object, unlocks the underlying mutex
(public member function) [edit]
operator=
[deleted]
not copy-assignable
(public member function) [edit]

[edit] Notes

A common beginner error is to "forget" to give a lock_guard variable a name, e.g. std::lock_guard(mtx); (which default constructs a lock_guard variable named mtx) or std::lock_guard{mtx}; (which constructs a prvalue object that is immediately destroyed), thereby not actually constructing a lock that holds a mutex for the rest of the scope.

std::scoped_lock offers an alternative for lock_guard that provides the ability to lock multiple mutexes using a deadlock avoidance algorithm.

(since C++17)

[edit] Example

Demonstrates safe and unsafe increments of a volatile variable by two threads.

#include <iostream>
#include <mutex>
#include <string_view>
#include <syncstream>
#include <thread>
 
volatile int g_i = 0;
std::mutex g_i_mutex;  // protects g_i
 
void safe_increment(int iterations)
{
    const std::lock_guard<std::mutex> lock(g_i_mutex);
    while (iterations-- > 0)
        g_i = g_i + 1;
    std::cout << "thread #" << std::this_thread::get_id() << ", g_i: " << g_i << '\n';
 
    // g_i_mutex is automatically released when lock goes out of scope
}
 
void unsafe_increment(int iterations)
{
    while (iterations-- > 0)
        g_i = g_i + 1;
    std::osyncstream(std::cout) << "thread #" << std::this_thread::get_id()
                                << ", g_i: " << g_i << '\n';
}
 
int main()
{
    auto test = [](std::string_view fun_name, auto fun)
    {
        g_i = 0;
        std::cout << fun_name << ":\nbefore, g_i: " << g_i << '\n';
        {
            std::jthread t1(fun, 1'000'000);
            std::jthread t2(fun, 1'000'000);
        }
        std::cout << "after, g_i: " << g_i << "\n\n";
    };
    test("safe_increment", safe_increment);
    test("unsafe_increment", unsafe_increment);
}

Possible output:

safe_increment:
before, g_i: 0
thread #140121493231360, g_i: 1000000
thread #140121484838656, g_i: 2000000
after, g_i: 2000000
 
unsafe_increment:
before, g_i: 0
thread #140121484838656, g_i: 1028945
thread #140121493231360, g_i: 1034337
after, g_i: 1034337

[edit] Defect reports

The following behavior-changing defect reports were applied retroactively to previously published C++ standards.

DR Applied to Behavior as published Correct behavior
LWG 2981 C++17 redundant deduction guide from lock_guard<Mutex> was provided removed

[edit] See also

implements movable mutex ownership wrapper
(class template) [edit]
deadlock-avoiding RAII wrapper for multiple mutexes
(class template) [edit]