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();
}