#include "pch.h"
#include <thread>
#include <atomic>
#include <mutex>
#include <windows.h>
mutex m;
queue<int32> q;
HANDLE handle;
void Producer() {
while (true) {
{
unique_lock<mutex> lock(m);
q.push(100);
}
::SetEvent(handle);
std::this_thread::sleep_for(100ms);
}
}
void Consumer() {
while (true) {
::WaitForSingleObject(handle, INFINITE);
//::ResetEvent(handle);
unique_lock<mutex> lock(m);
if (!q.empty()) {
int32 data = q.front();
q.pop();
cout << data << endl;
}
}
}
int main() {
handle = ::CreateEvent(NULL, FALSE, FALSE, NULL); // 보안적, signal auto or maunual, 초기 상태, 이름
std::thread t1(Producer);
std::thread t2(Consumer);
t1.join();
t2.join();
::CloseHandle(handle);
return 0;
}
커널모드에서 event를 생성하여 event의 상태가 signal상태라면 Consumer 함수의 WaitForSingleObject에서 빠져나오게 되고, non-signal 상태라면 커널 모드에서 thread가 대기하게 된다. 이렇게 함으로 cpu점유율을 줄일 수 있다.
#include "pch.h"
#include <thread>
#include <atomic>
#include <mutex>
mutex m;
queue<int32> q;
condition_variable cv;
void Producer() {
while (true) {
{
unique_lock<mutex> lock(m);
q.push(100);
}
cv.notify_one();
}
}
void Consumer() {
while (true) {
unique_lock<mutex> lock(m);
cv.wait(lock, []() {return !q.empty();});
int32 data = q.front();
q.pop();
cout << data << endl;
}
}
int main() {
std::thread t1(Producer);
std::thread t2(Consumer);
t1.join();
t2.join();
return 0;
}
전의 코드에서 api부분을 없애고, c++표준을 사용한 condition_variable로 event를 사용할 수 있다.
cv.notify_one()으로 대기중인 하나의 스레드를 실행 가능하게 하고, cv.wait부분에서 lock과 조건을 확인해서 스레드가 다시 대기하거나, 실행된다. cv.wait()의 동작 순서는
- lock을 얻는 시도를 한다.
- lock을 얻었으면, 조건을 확인한다.
- 조건이 참이면 다음 코드를 이어서 수행하고, 거짓이면 lock을 풀고 대기 상태
만약에 대기상태가 되면, 다시 condition_variable에서 notify를 할 때까지 대기 상태가 되고, notify를 하면 대기 상태를 풀고 1번부터 다시 동작한다.
condition_variable을 사용할 때는 보통 lock을 잠구고 -> 공유 변수 값을 수정 -> lock을 풀고 -> notify로 사용한다.
'MMOServer' 카테고리의 다른 글
멀티스레드의 메모리 이슈 (0) | 2025.03.13 |
---|---|
Future(C++) (0) | 2025.03.13 |
SpinLock(C++) (0) | 2025.03.13 |
Mutex(C++) (0) | 2025.03.11 |
Atomic(C++) (0) | 2025.03.11 |