하아찡
[C++] Event 본문
안녕하세요
이번에는 Event처리를 해볼건데요.
이전에 사용했던 코드를 먼저 봅시다.
#include "pch.h"
#include <thread>
#include <atomic>
#include <mutex>
class AdaptiveLock {
public:
void lock() {
int32 spinCount = 0; // Spin 횟수
const int32 maxSpinCount = 100; // Spin 횟수 제한
bool expected = false;
bool desired = true;
while (!_locked.compare_exchange_weak(expected, desired)) {
expected = false;
if (++spinCount > maxSpinCount) {
//cout << "Sleep으로 변경" << endl;
//std::this_thread::yield();
std::this_thread::sleep_for(std::chrono::milliseconds(10000)); // Sleep 전환
}
else {
std::this_thread::yield(); // CPU 양보
}
}
}
void unlock() {
_locked.store(false);
}
private:
atomic<bool> _locked = false;
};
int32 sum = 0;
AdaptiveLock adaptiveLock;
queue<int32> q;
void Producer() {
while (true) {
{
unique_lock<AdaptiveLock> guard(adaptiveLock);
q.push(100);
}
std::this_thread::sleep_for(std::chrono::milliseconds(1000000));
}
}
void Consumer() {
while (true) {
unique_lock<AdaptiveLock> guard(adaptiveLock);
if (q.empty() == false) {
//값이 있으면 빼온다
int32 data = q.front();
q.pop();
cout << data << endl;
}
}
}
int main()
{
mutex m;
std::thread t1(Producer);
std::thread t2(Consumer);
std::thread t3(Consumer);
std::thread t4(Consumer);
std::thread t5(Consumer);
if (t1.joinable()) {
t1.join();
}
if (t2.joinable()) {
t2.join();
}
if (t3.joinable()) {
t3.join();
}
if (t4.joinable()) {
t4.join();
}
if (t5.joinable()) {
t5.join();
}
cout << sum << endl;
}
데이터를 추가하는 코드가 생각보다 많이 작동하지않는 코드인데요. 대신 데이터를 불러오는 코드는 무수히 반복하는 코드입니다.
그래서 이전에 SpinLock을 돌다가 일정수준이 넘어가면 SleepLock으로 변경하는 코드로 작업을 하였는데요.
이러한 방식도 있지만, 데이터를 넣었을때 데이터를 넣었다라고 알려줄 수 있는 기능도 있습니다.
그래서 커널에다가 이벤트를 등록하고 사용을 하게됩니다.
#include "pch.h"
#include <thread>
#include <atomic>
#include <mutex>
#include <Windows.h>
class AdaptiveLock {
public:
void lock() {
int32 spinCount = 0; // Spin 횟수
const int32 maxSpinCount = 100; // Spin 횟수 제한
bool expected = false;
bool desired = true;
while (!_locked.compare_exchange_weak(expected, desired)) {
expected = false;
if (++spinCount > maxSpinCount) {
cout << "Sleep으로 변경" << endl;
//std::this_thread::yield();
std::this_thread::sleep_for(std::chrono::milliseconds(10000)); // Sleep 전환
}
else {
cout << "계속 잡고있냐?" << endl;
std::this_thread::yield(); // CPU 양보
}
}
}
void unlock() {
_locked.store(false);
}
private:
atomic<bool> _locked = false;
};
int32 sum = 0;
AdaptiveLock adaptiveLock;
HANDLE handle;
queue<int32> q;
mutex m;
void Producer() {
while (true) {
{
unique_lock<AdaptiveLock> guard(adaptiveLock);
q.push(100);
}
//이벤트호출!
::SetEvent(handle);
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
}
void Consumer(int32 index) {
while (true) {
//이벤트 호출 대기 - 무한정
::WaitForSingleObject(handle, INFINITE);
//::ResetEvent(handle); //이벤트모드가 TRUE일때 즉 수동리셋일때 활성화 시켜줘야함.
cout << index << endl;
unique_lock<AdaptiveLock> guard(adaptiveLock);
if (q.empty() == false) {
//값이 있으면 빼온다
int32 data = q.front();
q.pop();
cout << data << endl;
}
}
}
int main()
{
handle = ::CreateEvent(NULL/* 보안속성 */, FALSE /* 이벤트모드 */, FALSE /* 꺼진상태 */,NULL /* 이벤트이름 */);
std::thread t1(Producer);
std::thread t2(Consumer,1);
std::thread t3(Consumer,2);
std::thread t4(Consumer,3);
std::thread t5(Consumer,4);
if (t1.joinable()) {
t1.join();
}
if (t2.joinable()) {
t2.join();
}
if (t3.joinable()) {
t3.join();
}
if (t4.joinable()) {
t4.join();
}
if (t5.joinable()) {
t5.join();
}
cout << sum << endl;
}
위 코드를 보시면 HANDLE을 사용하기위해 #include<Windows.h>를 추가하셔야합니다.
::CreateEvent에 대한 파라미터 상세한 정보는 따로 찾아보시길 바랍니다
첫번째 -> 보안속성
두번째 -> 이벤트모드 (자동, 수동)
세번째 -> 이벤트상태
네번째 -> 이벤트이름
저는 여기서 두번째만 다루겠습니다.
자 이벤트모드를 자동으로 키기위해선 값이 FALSE가 필요합니다.
반대로 수동모드로 키기위해선 TRUE로 설정해줘야합니다.
자그럼 자동모드는 뭐고 수동모드는 뭐냐?
우리가 이벤트를 처음에 생성하고 바로 작동시키기위해서는 세번째 파라미터값을 건드려주면 됩니다.
이벤트를 설정해주면서 실행시키고싶으면 TRUE로 설정해주면되고, 내가 따로 이벤트를 호출하기전까지 작동을 시키지 않을꺼면 FALSE로 설정해주시면 됩니다.
갑자기 왜 세번째 파라미터를 설명하냐!
이벤트모드 설정인 해당 파라미터와 연관성이 있습니다.
자동모드로 설정을해서 이벤트를 호출하게될 경우 이벤트 상태값이 변경되는데 이는 변경시키고 다시 FALSE로 변경을 시켜줍니다. 자동모드에선 상관이없는데 수동모드에선 문제가 발생합니다.
수동모드에선 이벤트상태를 다시 리셋시켜줘야해서 ::ResetEvent함수를 호출해줘야합니다.
그리고 위 예시를 보면 특정 줄들이 주석처리가 되어있는데 이벤트모드가 수동으로 처리가됐을 풀어줘야합니다.
자 그리고 우리가 궁굼해야할 점들이 있습니다.
이벤트 호출(SetEvent)은 한곳에서만 해주는데 여러 쓰레드가 한번에 이벤트를 기다리고 있네요?
한번에 다 동시에 실행이 될까요?
정답은 이벤트모드 설정에 따라 다르게 됩니다.
우리가 자동모드로 선택하여 이벤트를 만들어줬다면 한번에 한개의 이벤트만 작동하게 됩니다. 대신 순서가 정해져있지요.
수동모드로 선택하여 이벤트를 만들어줬다면, 모든이벤트가 한번에 작동 할 수도 있습니다!
무조건적으로 다 작동하는것은 아닙니다.
결과값을 이미지로 보여드리겠습니다.

수동모드로 변경했을때는

위 이미지들처럼 수동모드와 자동모드에 대한 이벤트 처리방식을 살펴보았습니다.
'C++ > 추가공부' 카테고리의 다른 글
[C++] 메모리 모델 (0) | 2024.12.27 |
---|---|
[C++ 11] condition variable(조건 변수) (0) | 2024.12.27 |
[C++ 11] SleepLock (1) | 2024.12.27 |
[C++ 11]std::atomic, SpinLock (1) | 2024.12.27 |
Volatile 예약어 (0) | 2024.10.30 |