Difference between revisions of "cpp/thread/shared mutex"
m (The explanation comment said that function increment() is followed by get() function) |
m (Undo revision 153274 by 172.71.98.62 (talk) the story behind: https://wg21.link/N4508 - in 2014 shared_mutex was renamed to shared_timed_mutex...) |
||
(2 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
{{cpp/title|shared_mutex}} | {{cpp/title|shared_mutex}} | ||
{{cpp/thread/shared_mutex/navbar}} | {{cpp/thread/shared_mutex/navbar}} | ||
− | {{ddcl | header=shared_mutex | since=c++17 | 1= | + | {{ddcl|header=shared_mutex|since=c++17|1= |
class shared_mutex; | class shared_mutex; | ||
}} | }} | ||
Line 25: | Line 25: | ||
===Member types=== | ===Member types=== | ||
{{dsc begin}} | {{dsc begin}} | ||
− | {{dsc hitem | Member type | Definition}} | + | {{dsc hitem|Member type|Definition}} |
− | {{dsc inc | cpp/thread/dsc native_handle_type | shared_mutex}} | + | {{dsc inc|cpp/thread/dsc native_handle_type|shared_mutex}} |
{{dsc end}} | {{dsc end}} | ||
===Member functions=== | ===Member functions=== | ||
{{dsc begin}} | {{dsc begin}} | ||
− | {{dsc inc | cpp/thread/mutex/dsc constructor | shared_mutex}} | + | {{dsc inc|cpp/thread/mutex/dsc constructor|shared_mutex}} |
− | {{dsc inc | cpp/thread/mutex/dsc destructor | shared_mutex}} | + | {{dsc inc|cpp/thread/mutex/dsc destructor|shared_mutex}} |
− | {{dsc inc | cpp/thread/mutex/dsc operator{{=}}}} | + | {{dsc inc|cpp/thread/mutex/dsc operator{{=}}}} |
− | {{dsc h2 | Exclusive locking}} | + | {{dsc h2|Exclusive locking}} |
− | {{dsc inc | cpp/thread/mutex/dsc lock | shared_mutex}} | + | {{dsc inc|cpp/thread/mutex/dsc lock|shared_mutex}} |
− | {{dsc inc | cpp/thread/mutex/dsc try_lock | shared_mutex}} | + | {{dsc inc|cpp/thread/mutex/dsc try_lock|shared_mutex}} |
− | {{dsc inc | cpp/thread/mutex/dsc unlock | shared_mutex}} | + | {{dsc inc|cpp/thread/mutex/dsc unlock|shared_mutex}} |
− | {{dsc h2 | Shared locking}} | + | {{dsc h2|Shared locking}} |
− | {{dsc inc | cpp/thread/mutex/dsc lock_shared | shared_mutex}} | + | {{dsc inc|cpp/thread/mutex/dsc lock_shared|shared_mutex}} |
− | {{dsc inc | cpp/thread/mutex/dsc try_lock_shared | shared_mutex}} | + | {{dsc inc|cpp/thread/mutex/dsc try_lock_shared|shared_mutex}} |
− | {{dsc inc | cpp/thread/mutex/dsc unlock_shared | shared_mutex}} | + | {{dsc inc|cpp/thread/mutex/dsc unlock_shared|shared_mutex}} |
− | {{dsc h2 | Native handle}} | + | {{dsc h2|Native handle}} |
− | {{dsc inc | cpp/thread/mutex/dsc native_handle | shared_mutex}} | + | {{dsc inc|cpp/thread/mutex/dsc native_handle|shared_mutex}} |
{{dsc end}} | {{dsc end}} | ||
===Example=== | ===Example=== | ||
{{example | {{example | ||
− | + | |The output below was generated on a single-core machine. When {{tt|thread1}} starts, it enters the loop for the first time and calls {{tt|increment()}} followed by {{tt|get()}}. However, before it can print the returned value to {{c/core|std::cout}}, the scheduler puts {{tt|thread1}} to sleep and wakes up {{tt|thread2}}, which obviously has time enough to run all three loop iterations at once. Back to {{tt|thread1}}, still in the first loop iteration, it finally prints its local copy of the counter's value, which is {{c|1}}, to {{tt|std::cout}} and then runs the remaining two loop iterations. On a multi-core machine, none of the threads is put to sleep and the output is more likely to be in ascending order. | |
− | + | |code= | |
#include <iostream> | #include <iostream> | ||
#include <mutex> | #include <mutex> | ||
#include <shared_mutex> | #include <shared_mutex> | ||
+ | #include <syncstream> | ||
#include <thread> | #include <thread> | ||
− | class ThreadSafeCounter { | + | class ThreadSafeCounter |
− | + | { | |
− | + | public: | |
+ | ThreadSafeCounter() = default; | ||
− | + | // Multiple threads/readers can read the counter's value at the same time. | |
− | + | unsigned int get() const | |
− | std::shared_lock lock(mutex_); | + | { |
− | + | std::shared_lock lock(mutex_); | |
− | + | return value_; | |
+ | } | ||
− | + | // Only one thread/writer can increment/write the counter's value. | |
− | + | void increment() | |
− | std::unique_lock lock(mutex_); | + | { |
− | + | std::unique_lock lock(mutex_); | |
− | + | ++value_; | |
+ | } | ||
− | + | // Only one thread/writer can reset/write the counter's value. | |
− | + | void reset() | |
− | std::unique_lock lock(mutex_); | + | { |
− | + | std::unique_lock lock(mutex_); | |
− | + | value_ = 0; | |
+ | } | ||
− | + | private: | |
− | + | mutable std::shared_mutex mutex_; | |
− | + | unsigned int value_{}; | |
}; | }; | ||
− | int main() { | + | int main() |
− | + | { | |
+ | ThreadSafeCounter counter; | ||
− | + | auto increment_and_print = [&counter]() | |
− | for (int i | + | { |
− | + | for (int i{}; i != 3; ++i) | |
− | + | { | |
+ | counter.increment(); | ||
+ | std::osyncstream(std::cout) | ||
+ | << std::this_thread::get_id() << ' ' << counter.get() << '\n'; | ||
+ | } | ||
+ | }; | ||
− | + | std::thread thread1(increment_and_print); | |
− | + | std::thread thread2(increment_and_print); | |
− | + | ||
− | + | ||
− | + | thread1.join(); | |
− | + | thread2.join(); | |
− | + | ||
− | + | ||
− | + | ||
} | } | ||
− | + | |p=true | |
− | + | |output= | |
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
123084176803584 2 | 123084176803584 2 | ||
123084176803584 3 | 123084176803584 3 | ||
Line 126: | Line 122: | ||
===See also=== | ===See also=== | ||
{{dsc begin}} | {{dsc begin}} | ||
− | {{dsc inc | cpp/thread/dsc shared_timed_mutex}} | + | {{dsc inc|cpp/thread/dsc shared_timed_mutex}} |
− | {{dsc inc | cpp/thread/dsc shared_lock}} | + | {{dsc inc|cpp/thread/dsc shared_lock}} |
− | {{dsc inc | cpp/thread/dsc unique_lock}} | + | {{dsc inc|cpp/thread/dsc unique_lock}} |
{{dsc end}} | {{dsc end}} | ||
{{langlinks|es|ja|ru|zh}} | {{langlinks|es|ja|ru|zh}} |
Latest revision as of 05:06, 13 June 2023
Defined in header <shared_mutex>
|
||
class shared_mutex; |
(since C++17) | |
The shared_mutex
class is a synchronization primitive that can be used to protect shared data from being simultaneously accessed by multiple threads. In contrast to other mutex types which facilitate exclusive access, a shared_mutex has two levels of access:
- shared - several threads can share ownership of the same mutex.
- exclusive - only one thread can own the mutex.
If one thread has acquired the exclusive lock (through lock, try_lock), no other threads can acquire the lock (including the shared).
If one thread has acquired the shared lock (through lock_shared, try_lock_shared), no other thread can acquire the exclusive lock, but can acquire the shared lock.
Only when the exclusive lock has not been acquired by any thread, the shared lock can be acquired by multiple threads.
Within one thread, only one lock (shared or exclusive) can be acquired at the same time.
Shared mutexes are especially useful when shared data can be safely read by any number of threads simultaneously, but a thread may only write the same data when no other thread is reading or writing at the same time.
The shared_mutex
class satisfies all requirements of SharedMutex and StandardLayoutType.
Contents |
[edit] Member types
Member type | Definition |
native_handle_type (optional*)
|
implementation-defined |
[edit] Member functions
constructs the mutex (public member function) | |
destroys the mutex (public member function) | |
operator= [deleted] |
not copy-assignable (public member function) |
Exclusive locking | |
locks the mutex, blocks if the mutex is not available (public member function) | |
tries to lock the mutex, returns if the mutex is not available (public member function) | |
unlocks the mutex (public member function) | |
| |
locks the mutex for shared ownership, blocks if the mutex is not available (public member function) | |
tries to lock the mutex for shared ownership, returns if the mutex is not available (public member function) | |
unlocks the mutex (shared ownership) (public member function) | |
Native handle | |
returns the underlying implementation-defined native handle object (public member function) |
[edit] Example
The output below was generated on a single-core machine. When thread1
starts, it enters the loop for the first time and calls increment()
followed by get()
. However, before it can print the returned value to std::cout, the scheduler puts thread1
to sleep and wakes up thread2
, which obviously has time enough to run all three loop iterations at once. Back to thread1
, still in the first loop iteration, it finally prints its local copy of the counter's value, which is 1, to std::cout
and then runs the remaining two loop iterations. On a multi-core machine, none of the threads is put to sleep and the output is more likely to be in ascending order.
#include <iostream> #include <mutex> #include <shared_mutex> #include <syncstream> #include <thread> class ThreadSafeCounter { public: ThreadSafeCounter() = default; // Multiple threads/readers can read the counter's value at the same time. unsigned int get() const { std::shared_lock lock(mutex_); return value_; } // Only one thread/writer can increment/write the counter's value. void increment() { std::unique_lock lock(mutex_); ++value_; } // Only one thread/writer can reset/write the counter's value. void reset() { std::unique_lock lock(mutex_); value_ = 0; } private: mutable std::shared_mutex mutex_; unsigned int value_{}; }; int main() { ThreadSafeCounter counter; auto increment_and_print = [&counter]() { for (int i{}; i != 3; ++i) { counter.increment(); std::osyncstream(std::cout) << std::this_thread::get_id() << ' ' << counter.get() << '\n'; } }; std::thread thread1(increment_and_print); std::thread thread2(increment_and_print); thread1.join(); thread2.join(); }
Possible output:
123084176803584 2 123084176803584 3 123084176803584 4 123084185655040 1 123084185655040 5 123084185655040 6
[edit] See also
(C++14) |
provides shared mutual exclusion facility and implements locking with a timeout (class) |
(C++14) |
implements movable shared mutex ownership wrapper (class template) |
(C++11) |
implements movable mutex ownership wrapper (class template) |