Difference between revisions of "cpp/thread/lock guard"
(Added some stdout logging) |
(→Notes: add the other case from scoped_lock) |
||
(31 intermediate revisions by 15 users not shown) | |||
Line 1: | Line 1: | ||
{{cpp/title|lock_guard}} | {{cpp/title|lock_guard}} | ||
{{cpp/thread/lock_guard/navbar}} | {{cpp/thread/lock_guard/navbar}} | ||
− | {{ddcl | header=mutex | 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 {{tt|lock_guard}} is a mutex wrapper that provides a convenient [[ | + | 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. |
− | When a {{tt|lock_guard}} object is created, it attempts to take ownership of the mutex it is given. | + | 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. | The {{tt|lock_guard}} class is non-copyable. | ||
Line 14: | Line 14: | ||
===Template parameters=== | ===Template parameters=== | ||
{{par begin}} | {{par begin}} | ||
− | {{par | Mutex | the type of the mutex to lock. The type must meet the {{ | + | {{par|Mutex|the type of the mutex to lock. The type must meet the {{named req|BasicLockable}} requirements}} |
{{par end}} | {{par end}} | ||
===Member types=== | ===Member types=== | ||
{{dsc begin}} | {{dsc begin}} | ||
− | {{dsc hitem | Member type | Definition}} | + | {{dsc hitem|Member type|Definition}} |
− | {{dsc | {{tt|mutex_type}} | Mutex}} | + | {{dsc|{{tt|mutex_type}}|Mutex}} |
{{dsc end}} | {{dsc end}} | ||
===Member functions=== | ===Member functions=== | ||
{{dsc begin}} | {{dsc begin}} | ||
− | {{dsc inc | cpp/thread/lock_guard/dsc constructor}} | + | {{dsc inc|cpp/thread/lock_guard/dsc constructor}} |
− | {{dsc inc | cpp/thread/lock_guard/dsc destructor}} | + | {{dsc inc|cpp/thread/lock_guard/dsc destructor}} |
+ | {{dsc inc|cpp/thread/lock_guard/dsc operator{{=}}}} | ||
{{dsc end}} | {{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 |
− | + | |Demonstrates safe and unsafe increments of a volatile variable by two threads. | |
− | + | |code= | |
− | + | ||
#include <iostream> | #include <iostream> | ||
+ | #include <mutex> | ||
+ | #include <string_view> | ||
+ | #include <syncstream> | ||
+ | #include <thread> | ||
− | int g_i = 0; | + | volatile int g_i = 0; |
std::mutex g_i_mutex; // protects g_i | std::mutex g_i_mutex; // protects g_i | ||
− | void safe_increment() | + | void safe_increment(int iterations) |
{ | { | ||
− | std::lock_guard<std::mutex> lock(g_i_mutex); | + | 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() | int main() | ||
{ | { | ||
− | std::cout << | + | 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
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) | |
destructs the lock_guard object, unlocks the underlying mutex (public member function) | |
operator= [deleted] |
not copy-assignable (public member function) |
[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 |
(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
(C++11) |
implements movable mutex ownership wrapper (class template) |
(C++17) |
deadlock-avoiding RAII wrapper for multiple mutexes (class template) |