2023-06-30에 작성된 소스코드입니다.
여러 개의 스레드가 하나의 벡터에 원소를 집어넣을 경우 에러가 발생한다. 벡터의 크기가 원소의 개수보다 많을 경우 새 메모리를 할당하고 데이터를 다시 쓰게 되는데 이 과정에서 2개 이상의 스레드가 동시에 메모리를 지우면서 에러가 발생한다.
이전에 배운 atomic
의 경우, 데이터를 쓰고 불러오는, 즉 load
와 store
만 사용할 수 있기 때문에 vector
와 같은 STL을 사용할 수 없다.
Atomic 대신 Lock을 쓰자.
그래서 2개 이상의 스레드가 하나의 메모리를 참조할 때, Lock
을 걸어 한 스레드가 메모리를 참조하고 있음을 알려줌으로써 다른 스레드가 참조하지 못 하게 할 수 있다. mutex
를 통해 Lock
을 걸 수 있다.
#include "pch.h"
#include <iostream>
#include "CorePch.h"
#include <thread>
#include <atomic>
#include <mutex>
vector<int32> v;
// Mutual Exclusive(상호배타적)
mutex m;
// RAII (Resource Acquisition Is Initialization)
template<typename T>
class LockGuard
{
public:
LockGuard(T& m)
{
_mutex = &m;
_mutex->lock();
}
~LockGuard()
{
_mutex->unlock();
}
private:
T* _mutex;
};
void Push()
{
for (int32 i = 0; i < 10000; i++)
{
// 자물쇠 잠근다. 다른 사람들이 못 쓰게!
// 자동문임. 생성자에서 락을 건 뒤, 실행이 끝나면 자동으로 소멸되면서 잠금 해제
LockGuard<mutex> lockguard(m);
//lock_guard<mutex> lockGuard(m); // 위와 같음
//unique_lock<mutex> uniqueLock(m, defer_lock); // 잠금 시점을 조정할 수 있음. 그 외엔 똑같음.
//uniqueLock.lock();
//m.lock();
v.push_back(i);
// 자물쇠를 푼다. 이제 다른 사람들도 사용할 수 있다.
//m.unlock();
}
}
int main()
{
v.reserve(20000);
thread t1(Push);
thread t2(Push);
t1.join();
t2.join();
cout << v.size() << endl;
}
LockGuard
: 직접 구현한 클래스이며, 생성자 호출과 함께 Lock
을 걸며, 소멸하면 해제된다. 즉, 선언과 동시에 락을 걸고 생명주기가 끝나면 자동으로 락이 풀린다.void Push
: 벡터에 원소를 추가하는 함수이며, 추가할 때마다 LockGuard
를 통해 메모리 참조를 제어한다.