- 프로세스 간의 동기화와 유저모드간의 동기화 모두 가능하다 (유저모드 모단 느리다)
- 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 방식 으로 지정, false 면 자동모드, ture 면 수동
//초기 값
//이름은 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;
}
'운영체제 & 병렬처리 > Multithread' 카테고리의 다른 글
condition_variable 예제 (Produce, Consumer) (0) | 2022.09.11 |
---|---|
Conditional Variable - basic (0) | 2022.09.10 |
Sleep 함수의 이해 (0) | 2022.09.09 |
Spinlock, 구현해 보기 Lock 구현 (0) | 2022.09.08 |
mutex 동시에 lock 하기 (0) | 2022.09.07 |