336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.
Threads allow you to execute multiple functions at the same time but it is often necessary that these functions access shared resources. Access to shared resources must be synchronized so that at time, only one thread would be able to read or write from or to the shared resource.
std::mutex
is the most commonly used mutex type; it is illustrated in the preceding code snippet. It provides methods to acquire and release the mutex. lock()
tries to acquire the mutex and blocks it if it is not available, try_lock()
tries to acquire the mutex and returns it without blocking if the mutex is not available, and unlock()
releases the mutex.std::timed_mutex
is similar to std::mutex
but provides two more methods to acquire the mutex using a timeout: try_lock_for()
tries to acquire the mutex and returns it if the mutex is not made available during the specified duration, and try_lock_until()
tries to acquire the mutex and returns it if the mutex is not made available until a specified time point.std::recursive_mutex
is similar to std::mutex
, but the mutex can be acquired multiple times from the same thread without being blocked.std::recursive_timed_mutex
is a combination of a recursive mutex and a timed mutex.
try_lock Example
#include
#include
#include
#include // std::cout
std::chrono::milliseconds interval(1000);
std::mutex mutex;
int job_shared = 0; // both threads can modify 'job_shared',
// mutex will protect this variable
int job_exclusive = 0; // only one thread can modify 'job_exclusive'
// no protection needed
// this thread can modify both 'job_shared' and 'job_exclusive'
void job_1()
{
std::this_thread::sleep_for(interval); // let 'job_2' take a lock
while (true) {
// try to lock mutex to modify 'job_shared'
if (mutex.try_lock()) {
std::cout << "job shared (" << job_shared << ")\n";
mutex.unlock();
return;
}
else {
// can't get lock to modify 'job_shared'
// but there is some other work to do
++job_exclusive;
std::cout << "job exclusive (" << job_exclusive << ")\n";
std::this_thread::sleep_for(interval);
}
}
}
// this thread can modify only 'job_shared'
void job_2()
{
mutex.lock();
std::this_thread::sleep_for(5 * interval);
++job_shared;
mutex.unlock();
}
int main()
{
std::thread thread_1(job_1);
std::thread thread_2(job_2);
thread_1.join();
thread_2.join();
}
job exclusive (1)
job exclusive (2)
job exclusive (3)
job exclusive (4)
job shared (1)
std::lock_guard is the locking mechanism seen earlier; it represents a mutex wrapper implemented in an RAII manner. it attempts to acquire the mutex at the time of it's construction and release it upon destruction.
template <class M>
class lock_guard
{
public:
typedef M mutex_type;
explicit lock_guard(M& Mtx) : mtx(Mtx)
{
mtx.lock();
}
lock_guard(M& Mtx, std::adopt_lock_t) : mtx(Mtx)
{ }
~lock_guard() noexcept
{
mtx.unlock();
}
lock_guard(const lock_guard&) = delete;
lock_guard& operator=(const lock_guard&) = delete;
private:
M& mtx;
}; |
unique_lockstd::unique_lock
is a mutex ownership wrapper that provides support for deferred locking, time locking, recursive locking, transfer of ownership, and using it with condition variables. This is available in C++11.
#include
#include
#include
struct Box {
explicit Box(int num) : num_things{num} {}
int num_things;
std::mutex m;
};
void transfer(Box &from, Box &to, int num)
{
// don't actually take the locks yet
std::unique_lock lock1(from.m, std::defer_lock);
std::unique_lock lock2(to.m, std::defer_lock);
// lock both unique_locks without deadlock
std::lock(lock1, lock2);
from.num_things -= num;
to.num_things += num;
// 'from.m' and 'to.m' mutexes unlocked in 'unique_lock' dtors
}
int main()
{
Box acc1(100);
Box acc2(50);
std::thread t1(transfer, std::ref(acc1), std::ref(acc2), 10);
std::thread t2(transfer, std::ref(acc2), std::ref(acc1), 5);
t1.join();
t2.join();
}
.
shared_lock std::shared_lock
is a mutex-shared ownership wrapper that provides support for deferred locking, time locking, and transfer of ownership. This is available in C++14.
#include
#include
#include
#include
std::shared_timed_mutex m;
int i = 10;
void read()
{
// both the threads get access to the integer i
std::shared_lock slk(m);
std::cout << "read i as " << i << "...\n"; // this is not synchronized
std::this_thread::sleep_for(std::chrono::milliseconds(10));
std::cout << "woke up...\n";
}
int main()
{
std::thread r1(read);
std::thread r2(read);
r1.join();
r2.join();
return 0;
}
read i as read i as 10...
10...
woke up...
woke up...
#include
#include
#include
#include
std::mutex g_mutex;
template
struct container
{
std::mutex mutex;
std::vector data;
};
void thread_func()
{
using namespace std::chrono_literals;
{
std::lock_guard lock(g_mutex);
std::cout << "running thread "
<< std::this_thread::get_id() << std::endl;
}
// Provides a hint to the implementation to reschedule the execution of threads, allowing other threads to run.
std::this_thread::yield();
std::this_thread::sleep_for(2s);
{
std::lock_guard lock(g_mutex);
std::cout << "done in thread "
<< std::this_thread::get_id() << std::endl;
}
}
// Lock the mutexes at the same time using a deadlock avoidance algorithm with std::lock():
template
void move_between(container & c1, container & c2, T const value)
{
std::lock_guard l1(c1.mutex);
std::lock_guard l2(c2.mutex);
c1.data.erase(
std::remove(c1.data.begin(), c1.data.end(), value),
c1.data.end());
c2.data.push_back(value);
}
int main()
{
std::thread t0(thread_func);
t0.join();
container c1;
c1.data.push_back(1);
c1.data.push_back(2);
c1.data.push_back(3);
container c2;
c2.data.push_back(4);
c2.data.push_back(5);
c2.data.push_back(6);
std::thread t1(move_between, std::ref(c1), std::ref(c2), 3);
std::thread t2(move_between, std::ref(c2), std::ref(c1), 6);
t1.join();
t2.join();
return 0;
}
condition_variable Mutexes are synchronization primitives that can be used to protect access to shared data. However, the standard library provides a synchronization primitive called a condition variable that enables a thread to signal to others that a certain condition has occurred. The thread or the threads that are waiting on the condition variable are blocked until the condition variable is signaled or until a timeout or a spurious wakeup occurs
#include
#include
#include
#include
std::condition_variable cv;
std::mutex cv_mutex;
std::mutex io_mutex;
int data = 0;
int main() {
std::thread p([&]() {
// simulate long running operation
{
using namespace std::chrono_literals;
std::this_thread::sleep_for(2s);
}
// produce
{
std::unique_lock lock(cv_mutex);
data = 42;
}
// print message
{
std::lock_guard l(io_mutex);
std::cout << "produced " << data << std::endl;
}
// continued from 4.
cv.notify_one();
});
std::thread c([&]() {
// wait for notification
{
std::unique_lock lock(cv_mutex);
cv.wait(lock);
}
// continued from 6.
{
std::lock_guard lock(io_mutex);
std::cout << "consumed " << data << std::endl;
}
});
p.join();
c.join();
return 0;
}