Server/C++
Lock을 사용한 Stack과 Queue 템플릿
Juzdalua
2024. 8. 1. 00:14
기본적으로 제공하는 Stack과 Queue 템플릿을 멀티스레드 환경에서 사용해보자.
#include "pch.h"
#include "CorePch.h"
#include <iostream>
#include <thread>
#include <mutex>
mutex m;
queue<int32> q;
stack<int32> s;
void Push() {
while (true) {
lock_guard<mutex> lock(m);
int32 value = rand() % 10;
q.push(value);
this_thread::sleep_for(10ms);
}
}
void Pop() {
while (true) {
lock_guard<mutex> lock(m);
if (!q.empty()) {
int32 data = q.front();
q.pop();
cout << data << endl;
}
}
}
int main()
{
thread t1(Push);
thread t2(Pop);
t1.join();
t2.join();
}
Stack이나 Queue 자료구조에 Push, Pop을 할 수 있는 간단한 함수를 만들었지만 용도에 따라서 직접 해당 작업을 수행할 경우에 mutex 가드를 매번 붙여줘야하는 작업이 필요하다.
실수가 발생할 수 있으니, 내부적으로 lock을 포함한 자료구조를 템플릿으로 만들어보자.
기본 생성자가 아닌 생성자와 대입 연산자는 사용할 수 없게 했다.
// ConcurrentStack.h
#pragma once
#include <mutex>
template <typename T>
class LockStack {
public:
LockStack() {}
LockStack(const LockStack&) = delete;
LockStack& operator=(const LockStack&) = delete;
void Empty() {
lock_guard<mutex> lock(_mutex);
return _stack.empty();
}
void Push(T value) {
lock_guard<mutex> lock(_mutex);
_stack.push(move(value));
_cv.notify_one();
}
bool TryPop(T& value) {
lock_guard<mutex> lock(_mutex);
if (_stack.empty())
return false;
value = move(_stack.top());
_stack.pop();
return true;
}
void WaitPop(T& value) {
unique_lock<mutex> lock(_mutex);
_cv.wait(lock, [this] {return _stack.empty() == false;});
value = std::move(_stack.top());
_stack.pop();
}
private:
stack<T> _stack;
mutex _mutex;
condition_variable _cv;
};
// ConcurrentQueue.h
#pragma once
#include <mutex>
template <typename T>
class LockQueue {
public:
LockQueue() {}
LockQueue(const LockQueue&) = delete;
LockQueue& operator=(const LockQueue&) = delete;
void Push(T value) {
lock_guard<mutex> lock(_mutex);
_queue.push(std::move(value));
_cv.notify_one();
}
bool TryPop(T& value) {
lock_guard<mutex> lock(_mutex);
if (_queue.empty())
return false;
value = std::move(_queue.front());
_queue.pop();
return true;
}
void WaitPop(T& value) {
unique_lock<mutex> lock(_mutex);
_cv.wait(lock, [this] {return _queue.empty() == false;});
value = std::move(_queue.front());
_queue.pop();
}
private:
queue<T> _queue;
mutex _mutex;
condition_variable _cv;
};
// main.cpp
#include "ConcurrentQueue.h"
#include "ConcurrentStack.h"
LockQueue<int32> lq;
LockStack<int32> ls;
void PushLock() {
while (true) {
int32 value = rand() % 10;
lq.Push(value);
this_thread::sleep_for(10ms);
}
}
void PopLock() {
while (true) {
int32 data = 0;
if (lq.TryPop(data))
cout << data << endl;
}
}
int main()
{
thread t1(PushLock);
thread t2(PopLock);
t1.join();
t2.join();
}