락이 걸렸을때, 스레드 차원에서 대응방법은?

  1. 무작정 기다린다. (스핀락)

  2. 다른일을 하다가 다시 시도한다. (일정시간 Sleep)

3. 락이 끝났을때 통보 받기. (이벤트를 사용하는 방법)

3번, 이벤트에 대해 알아본다.

아래는 생산자-소비자 스레드 예제로, 생산자 스레드에서는 락을걸고 큐에 데이터를 넣고 소비자 스레드에서는 락을 걸고 큐에 데이터를 꺼내 특정 작업을 한다고 가정한다.

//

#include <mutex>
#include <atomic>
#include <chrono>
#include <thread>
#include <iostream>
#include <queue>

using namespace std;

mutex m;
queue<int> q;

// 생산자 스레드
void Producter()
{
	while (true)
	{
		{
			// 락을걸고 q에 데이터를 넣음
			unique_lock <mutex> lock(m);
			q.push(100);
		}
		this_thread::sleep_for(10000ms);
	}
}

// 소비자 스레드
void Consumer()
{
	while (true)
	{
		// 락을걸고 q에서 데이터를 꺼냄
		unique_lock<mutex> lock(m);
		if (q.empty() == false)
		{
			int data = q.front();
			q.pop();

			//특정 작업을 한다고 가정
			cout << data << '\\n';
		}
	}
}

int main()
{
	thread t1(Producter);
	thread t2(Consumer);

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

	return 0;
}

image.png

진단 도구를 보면, CPU사용률은 5%. 예제를 진행한 컴퓨터는 20코어 이므로 하나의 스레드만 수행되고 있는것을 확인할 수 있다. 여기서 수행되는 스레드는 소비자 스레드로, 이는 CPU리소스 낭비다.

즉 데이터가 있을때만 소비자 스레드에게 알려줘서 일을 진행하면 좋을 것.

//

#include <mutex>
#include <atomic>
#include <chrono>
#include <thread>
#include <iostream>
#include <queue>
#include <windows.h>

using namespace std;

mutex m;
queue<int> q;
HANDLE handle;

// 생산자 스레드
void Producter()
{
	while (true)
	{
		{
			// 락을걸고 q에 데이터를 넣음
			unique_lock <mutex> lock(m);
			q.push(100);
		}
		::SetEvent(handle); // 이벤트를 시그널상태로 바꾸기
		this_thread::sleep_for(10000ms);
	}
}

// 소비자 스레드
void Consumer()
{
	while (true)
	{
		::WaitForSingleObject(handle, INFINITE);
		// 수동리셋이라면 ResetEvent 호출해야함

		// 락을걸고 q에서 데이터를 꺼냄
		unique_lock<mutex> lock(m);
		if (q.empty() == false)
		{
			int data = q.front();
			q.pop();

			//특정 작업을 한다고 가정
			cout << data << '\\n';
		}
	}
}

int main()
{
	// 커널 오브젝트
	// Usage Count
	// Signal / Non-Signal
	// Auto , Manual Reset
	handle = ::CreateEvent(NULL, FALSE, FALSE, NULL); // 이벤트 생성

	thread t1(Producter);
	thread t2(Consumer);

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

	::CloseHandle(handle);
	return 0;
}

윈도우 이벤트 커널오브젝트를 적용한 예제. 이제 할일이 있을때 생산자 스레드가 알려주면, 소비자 스레드에서 이벤트를 확인해 일을 처리하는 구조가 되었다.

다시 측정해보면, CPU사용률이 0%인것을 확인할 수 있다.