MMOServer

Mutex(C++)

이야기prog 2025. 3. 11. 22:12
#include "pch.h"
#include <thread>
#include <atomic>
#include <mutex>

std::mutex m;
std::vector<int> v;
void Push() {
	for (int i = 1; i <= 10000; ++i)
		v.push_back(i);
}
int main()
{
	thread t1(Push);
	thread t2(Push);

	t1.join();
	t2.join();

	std::cout << v.size();
}

 위의 코드는 Push라는 함수를 thread를 이용해서 실행하는 코드인데, 실행하면 error가 뜰 것이다.

결론적으로 말하면 vector나 여러가지 자료구조는 Single Thread를 상정하고 만든 것이기 때문에 Multi Thread환경에서는 동작하면 여러가지 에러사항이 많다.

 

 vector의 구현만 살펴봐도 v에 push_back을 하면 배열에 값을 복사하다가 배열의 크기가 가득차면 구현에 맞게 더 큰 배열을 만들고 원래 배열값들을 복사한 뒤에 원래 배열을 지우는데, Push의 함수가 동시에 실행되고 있다면, 한 쪽의 Push에서  원래 배열을 지우고 다른 쪽의 Push에서 지운 배열에 접근하면 바로 Crash가 생긴다.

 

#include "pch.h"
#include <thread>
#include <atomic>
#include <mutex>

std::mutex m;
std::vector<int> v;
void Push() {
	for (int i = 1; i <= 10000; ++i) {
		m.lock();
		v.push_back(i);
		m.unlock();
	}
	
}
int main()
{
	thread t1(Push);
	thread t2(Push);

	t1.join();
	t2.join();

	std::cout << v.size();
}

 

 그래서 C++에서 제공하는 mutex를 사용하면 되는데, mutex를 lock하면 unlock해주지 않는 이상 그 공간을 다른 thread가 접근할 수 없게 한다. 즉 문을 잠구는 역할을 해준다.

 

여기서 unlock을 해주지 않으면 다른 쓰레드가 접근할 수 없기 때문에 실행이 끝나지 않게 될 수 있다.

여기서 재귀적으로 lock을 할 수 있게 하는 std::recursive_mutex가 있고, RAII(Resource Acquisition Is Initialization)를 적용한 std::lock_guard와 std::unique_lock등이 있다.

RAII가 적용된 lock을 사용하면 unlock을 할 필요없이 유효범위를 벗어나 소멸자가 실행되면 알아서 unlock을 실행해준다. 또한 unique_lock은 선언만 하고 실행을 나중에 할 수 있다는 장점이 있다.

 

lock한 공간은 사실상 single thread기 때문에 lock을 한 공간 Critical Section이 클 수록 효율이 떨어진다.

 

#include "pch.h"
#include <thread>
#include <atomic>
#include <mutex>

std::recursive_mutex m;
std::vector<int> v;
void Push() {
	for (int i = 1; i <= 10000; ++i) {
		//m.lock();
		//lock_guard<std::recursive_mutex> lockGuard(m);
		unique_lock<std::recursive_mutex> uniqueGuard(m, std::defer_lock);

		uniqueGuard.lock();
		v.push_back(i);
		//m.unlock();
	}
	
}
int main()
{
	thread t1(Push);
	thread t2(Push);

	t1.join();
	t2.join();

	std::cout << v.size();
}

'MMOServer' 카테고리의 다른 글

Future(C++)  (0) 2025.03.13
Event, Condition_Variable(C++)  (0) 2025.03.13
SpinLock(C++)  (0) 2025.03.13
Atomic(C++)  (0) 2025.03.11
Thread(C++)  (0) 2025.03.11