본문 바로가기
코드/C++

Lock

by bongin 2024. 11. 12.
728x90
반응형
#include <iostream>
using namespace std;
#include <thread>
#include <vector>
#include <Windows.h>
#include <atomic>
#include <mutex>

mutex m;
vector<int> v;

void Push()
{
	for (int i = 0; i < 10000; i++)
	{
		v.push_back(i);
	}
}

int main()
{
	thread t1(Push);
	thread t2(Push);

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

	cout << v.size() << endl;
}

-> crash

-> heap 영역에 동적 할당 받은 공간이 꽉 찰 경우 공간을 재할당 받아 데이터를 이동시키는 vector의 특징으로 인해

t1이 v[0]의 주소를 0x0001에서 0x0009로 재할당 받아 이동해 갔는데도 불구하고 t2는 여전히 0x0001을 가리키고 있고 여기에 값을 쓰려다 crash가 발생

 

 

#include <iostream>
using namespace std;
#include <thread>
#include <vector>
#include <Windows.h>
#include <atomic>
#include <mutex>

mutex m;
vector<int> v;

void Push()
{
	for (int i = 0; i < 10000; i++)
	{
		v.push_back(i);
	}
}

int main()
{
	v.reserve(20000);

	thread t1(Push);
	thread t2(Push);

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

	cout << v.size() << endl;
}

-> crash는 피했으나 출력 : 19999

-> [0][1][2][ ]

-> v[4]에 t1과 t2가 동시에 push_back(3)을 할 경우 발생

 

 

#include <iostream>
using namespace std;
#include <thread>
#include <vector>
#include <Windows.h>
#include <atomic>
#include <mutex>

mutex m;
vector<int> v;

void Push()
{
	for (int i = 0; i < 10000; i++)
	{
		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;
}

-> 출력 : 20000, m.lock(), m.unlock() 사용

-> t1이 문을 잠그고 작업하는 동안 t2는 대기 함

 

#include <iostream>
using namespace std;
#include <thread>
#include <vector>
#include <Windows.h>
#include <atomic>
#include <mutex>

mutex m;
vector<int> v;

void Push()
{
	for (int i = 0; i < 10000; i++)
	{
		m.lock();
		v.push_back(i);

		if (i == 5000)
		{
			//m.unlick();
			break;
		}
		m.unlock();
	}
}

int main()
{
	v.reserve(20000);

	thread t1(Push);
	thread t2(Push);

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

	cout << v.size() << endl;
}

-> 반복문을 탈출할 때 m.unlock()을 생략하면 Push함수에서 m잠금을 해제하지 못함

 

#include <iostream>
using namespace std;
#include <thread>
#include <vector>
#include <Windows.h>
#include <atomic>
#include <mutex>

mutex m;
vector<int> v;

template<typename T>
class LockGuard
{
public:
	LockGuard(T& m) : _mutex(m)
	{
		_mutex.lock();
	}
	~LockGuard()
	{
		_mutex.unlock();
	}
private:
	T& _mutex;
};
void Push()
{
	for (int i = 0; i < 10000; i++)
	{
		LockGuard<mutex> lockGuard(m);
		v.push_back(i);

		if (i == 5000)
		{
			break;
		}
	}
}

int main()
{
	v.reserve(20000);

	thread t1(Push);
	thread t2(Push);

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

	cout << v.size() << endl;
}

-> 생성자 (LockGuard<mutex> lockGuard(m))에서 잠그고 for를 빠져나갈 때 소멸자에서 푼다.

 

(변형)

#include <iostream>
using namespace std;
#include <thread>
#include <vector>
#include <Windows.h>
#include <atomic>
#include <mutex>

mutex m;
vector<int> v;

template<typename T>
class LockGuard
{
public:
	LockGuard(T& m) : _mutex(m)
	{
		_mutex.lock();
	}
	~LockGuard()
	{
		_mutex.unlock();
	}
private:
	T& _mutex;
};
void Push()
{
	cout << "in" << endl;
	for (int i = 0; i < 10000; i++)
	{
		LockGuard<mutex> lockGuard(m);
		v.push_back(i);

		if (i == 9999)
		{
			cout << "out" << endl;
			break;
		}
	}
}

int main()
{
	v.reserve(20000);

	thread t1(Push);
	thread t2(Push);

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

	cout << v.size() << endl;
}

-> t1, t2 동시에 진입, 순차적으로 나감

 

* 표준 라이브러리에 이미 생성되어 있음, std::lock_guard<mutex> lockGuard(m);

 

 

(예제)

#include <iostream>
using namespace std;
#include <thread>
#include <vector>
#include <Windows.h>
#include <atomic>
#include <mutex>

mutex m;
vector<int> v;

template<typename T>
class LockGuard
{
public:
	LockGuard(T& m) : _mutex(m)
	{
		cout << "① 생성 : " << this_thread::get_id() << endl;
		_mutex.lock();
	}
	~LockGuard()
	{
		cout << "③ 파괴 : " << this_thread::get_id() << endl;
		_mutex.unlock();
	}
private:
	T& _mutex;
};
void Push()
{
	for (int i = 0; i < 10; i++)
	{
		LockGuard<mutex> lockGuard(m);
		cout << "② 실행 : " << this_thread::get_id() << endl;
		v.push_back(i);

		if (i == 9)
		{
			cout << "④ 퇴장" << endl;
			break;
		}
	}
}

int main()
{
	v.reserve(20000);

	thread t1(Push);
	thread t2(Push);

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

	cout << v.size() << endl;
}

->

1. cout << ⓐ << ⓑ << endl에서 ⓐ와 ⓑ가 분할되어 실행 (한줄의 코드를 2회에 나눠 작업함)

2. t1과 t2 스레드가 개별적으로 생성 -> 실행 -> 파괴의 순서로 실행됨

 

728x90
반응형

'코드 > C++' 카테고리의 다른 글

deadlock  (0) 2024.11.26
spin lock  (0) 2024.11.19
PE File Format, notepad  (0) 2024.06.10
release 배포 시 dll 오류 날 때  (2) 2023.11.21
std::to_wstring(int), _tow_s(int, wchar_t, 2 or 10);  (0) 2023.11.16

댓글