반응형
  • 프로세스 간의 동기화와 유저모드간의 동기화 모두 가능하다 (유저모드 모단 느리다)
  • spinlock 은 유저레벨에서 동기화 가능하다

 

이벤트에는 두가지가 있다

 

auto reset event

manual reset event

 

unique_lock 이글 참고

https://3dmpengines.tistory.com/2176

 

 

 

한쪽에선 클라이언트 데이터를 수신 받아와서 q에 밀어넣고 
게임 패킷과 관련된 스레드에서 q에서 데이터를 추출해오는 상황이라 가정하여 이를 작성하면 다음 코드 처럼 되고

 

#include "pch.h"
#include <iostream>
#include "CorePch.h"
#include <thread>	
#include <atomic>			//멀티 플랫폼에서 작동 가능
#include <vector>
#include <mutex>
#include "AccountManager.h"
#include "UserManager.h"
#include <chrono>
#include <queue>
#include "windows.h"

using namespace std;

mutex m;
queue<int32> q;

//한쪽에선 클라이언트 데이터를 수신 받아와서 q에 밀어넣고 
//게임 패킷과 관련된 스레드에서 q에서 데이터를 추출해오는 상황이라 가정

void producer()
{
	while (true)
	{
		{
			unique_lock<mutex> lock(m);
			q.push(100);
		}
		
		this_thread::sleep_for(100ms);
	}
}

void consumer()
{
	while (true)
	{
		{
			unique_lock<mutex> lock(m);
			if (q.empty() == false)
			{
				int32 data = q.front();
				q.pop();
				cout << data << std::endl;
			}
		}

		this_thread::sleep_for(100ms);
	}
}

int main()
{

	thread t1(producer);
	thread t2(consumer);

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


	return 0;
}

 

결과는 100ms 마다 q 에 데이터를 push 하고 consumer 함수에 데이터를 꺼내다 쓰는 상황이 된다

 

 

 

하지만 만야겡 입력이 엄청 긴시간 동일 일어나지 않고 어쩌다 한번씩 입력이 일어난다고 하면

consumer 스레드는 계속 lock 과 unlock 을 반복하고 있긴 하기 때문에 이 부분에 스레드가 차이하는 비용이 그냥 이 코드만 보면 높진 않지만 이런게 많이 쌓이면 성능에 좋지 않음으로 이 부분을 event 를 고려하여 처리 하면

더 효율적으로 작업 할 수 있다

 

현재는 테스트 pc 가 좋아서 이후 테스트 결과와 크게 차이가 없는 cpu 점유율 0 % 인데

성능이 낮은 PC 일 수록 이것이 8~10% 까지도 올라 갈 순 있다

 

 

처리 방식 : 데이터가 q 에 있는 상황 즉 consume 해도 되는 상황에서 consumer 스레드가 돌도록 한다

 

 

 

Event 는 유저모드가 아닌 커널모드에서 관리 되는것이기 떄문에 커널단에서 처리된다

 

//이벤트 속성 : NULL
//auto rest 방식 으로 지정
//초기 값
//이름은 null
//Event 는 유저모드가 아닌 커널모드에서 관리 되는것이기 떄문에 커널단에서 처리된다

handle = ::CreateEvent(nullptr, false, false, nullptr);  //non-signal 상태

 

handle 이 물고 있는건 커널오브젝트인데 커널에서 관리하는 이벤트 오브젝트라 보면 된다

 

커널오브젝트에는 Usage Count = 이 오브젝트를 몇명이 사용하고 있는지 

그리고 Signal / Non-Signal 두가지 중 하나의 상태를 갖고 있다 (bool)

true 명 Signal 상태

자동모드/수동모드 에 대한 정보(이벤트는 자동모드 수동모드가 있다)

(자동모드 : 이벤트를 바로 리셋 해주는 모드)

 

SetEvent(handle) 하면 Signla 상태가 된다

 

 

 

#include "pch.h"
#include <iostream>
#include "CorePch.h"
#include <thread>	
#include <atomic>			//멀티 플랫폼에서 작동 가능
#include <vector>
#include <mutex>
#include "AccountManager.h"
#include "UserManager.h"
#include <chrono>
#include <queue>
#include "windows.h"

using namespace std;

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

//한쪽에선 클라이언트 데이터를 수신 받아와서 q에 밀어넣고 
//게임 패킷과 관련된 스레드에서 q에서 데이터를 추출해오는 상황이라 가정

void producer()
{
	while (true)
	{
		{
			unique_lock<mutex> lock(m);
			q.push(100);
		}
		
		::SetEvent(handle);		//event 를 signal 상태로 만든다

		this_thread::sleep_for(100ms);
	}
}

void consumer()
{
	while (true)
	{
		//무한 대기, handle 가 Signal 상태가 될때까지
		::WaitForSingleObject(handle, INFINITE);					//evet 가 signal 상태가 되면 빠져나옴 이 함수를
		//즉 WaitForSingleObject 를 만나면 이 쓰레드는 wake up 되지 않고 즉 실행 되지 않고 수면 상태(sleep)로 빠져서 잠들게 된다
		//WaitForSingleObject 에 걸린 이벤트는 handle 자동모드라 WaitForSingleObject 이 함수가 실행된고 빠져나오는 즉시 event 가 non-signal 이 된다

		std::cout << "ConSumer 함수" << std::endl;
		{
			unique_lock<mutex> lock(m);
			if (q.empty() == false)
			{
				int32 data = q.front();
				q.pop();
				cout << data << std::endl;
			}
		}

		this_thread::sleep_for(100ms);
	}
}

int main()
{

	//커널오브젝트
	//Usage Count

	//이벤트 속성 : NULL
	//auto rest 방식 으로 지정
	//초기 값
	//이름은 null
	//Event 는 유저모드가 아닌 커널모드에서 관리 되는것이기 떄문에 커널단에서 처리된다
	handle = ::CreateEvent(nullptr, false, false, nullptr);

	thread t1(producer);
	thread t2(consumer);

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


	::CloseHandle(handle);

	return 0;
}

 

무한 대기, handle 가 Signal 상태가 될때까지
::WaitForSingleObject(handle, INFINITE); //evet 가 signal 상태가 되면 빠져나옴 이 함수를
즉 WaitForSingleObject 를 만나면 이 쓰레드는 wake up 되지 않고 즉 실행 되지 않고 수면 상태(sleep)로 빠져서 잠들게 된다, 즉 필요 없는 스레드가 돌아가지 않게 효율을 올릴 수 있다


WaitForSingleObject 에 걸린 이벤트는 handle 자동모드라 WaitForSingleObject 이 함수가 실행된고 빠져나오는 즉시 event 가 non-signal 이 된다

 

 

 

결과 또한 데이터가 있을때 Consumer 를 통해 소진 되는 것을 얼추 알 수 있다

즉 데이터가 있을때 Comsumer 함수가 실행 되도록 evet 로 실행 순서를 제어한 것

 

이렇게 처리 하면 CPU 점유율이 0 % 대로 떨어지는 것을 알 수 있다

 

 

 

결과는 같고 아래는 Event 가  수동모드이다

wait.. 을 빠져나온 다음 바로 ResetEvent(handle); 을 처리 해주면 자동모드와 동일하다

#include "pch.h"
#include <iostream>
#include "CorePch.h"
#include <thread>	
#include <atomic>			//멀티 플랫폼에서 작동 가능
#include <vector>
#include <mutex>
#include "AccountManager.h"
#include "UserManager.h"
#include <chrono>
#include <queue>
#include "windows.h"

using namespace std;

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

//한쪽에선 클라이언트 데이터를 수신 받아와서 q에 밀어넣고 
//게임 패킷과 관련된 스레드에서 q에서 데이터를 추출해오는 상황이라 가정

void producer()
{
	while (true)
	{
		{
			unique_lock<mutex> lock(m);
			q.push(100);
		}
		
		::SetEvent(handle);		//event 를 signal 상태로 만든다

		this_thread::sleep_for(100ms);
	}
}

void consumer()
{
	while (true)
	{
		//무한 대기, handle 가 Signal 상태가 될때까지
		::WaitForSingleObject(handle, INFINITE);
		::ResetEvent(handle);
		
	

		std::cout << "ConSumer 함수" << std::endl;
		{
			unique_lock<mutex> lock(m);
			if (q.empty() == false)
			{
				int32 data = q.front();
				q.pop();
				cout << data << std::endl;
			}
		}

		this_thread::sleep_for(100ms);
	}
}

int main()
{

	//커널오브젝트
	//Usage Count

	//이벤트 속성 : NULL
	//auto rest 방식 으로 지정
	//초기 값
	//이름은 null
	//Event 는 유저모드가 아닌 커널모드에서 관리 되는것이기 떄문에 커널단에서 처리된다
	handle = ::CreateEvent(nullptr, true, false, nullptr);

	thread t1(producer);
	thread t2(consumer);

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


	::CloseHandle(handle);

	return 0;
}

 

 

 

반응형

+ Recent posts