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;
}


'ⓟrogramming > C++' 카테고리의 다른 글

Reverse Sentesces  (0) 2018.01.16
스트림 연산  (0) 2018.01.16
[Thread] Working with threads  (0) 2018.01.04
[Thread] Handling exceptions from thread functions  (0) 2018.01.03
Visual Studio 에서 Boost 사용법  (0) 2016.07.28
블로그 이미지

뚱땡이 우주인

,