티스토리 뷰
mutex 기반으로 작성하고, condition variable로 단점을 보완해 본다
mutex 기반
틀
#include <mutex>
template<typename T>
class LockStack
{
public:
LockStack() {}
LockStack(const LockStack&) = delete;
LockStack& operator=(const LockStack&) = delete;
private:
stack<T> _stack;
mutex _mutex;
};
push 연산
void Push(T value)
{
lock_guard<mutex> lock(_mutex);
_stack.push(std::move(value));
}
- lock_guard 이용
- cf) 이동이 가능한 것들은, move를 이용해서 빠른 연산 지원
pop 연산
- 고전적으로는 empty() 연산으로 체크 후, top, pop 실행: empty → top → pop
- 하지만, 멀티 스레드 환경이라면 데이터 불일치 상황이 가능함 (다른 스레드가 중간에 연산하는 경우가 가능)
bool TryPop(&outValue)
{
lock_guard<mutex> lock(_mutex);
if (_stack.empty())
{
return false;
}
outValue = std::move(_stack.top());
_stack.pop();
return true;
}
- mutex로 락을 걸어두고, empty → top → pop
- 지금 구현에서는 한 번에 꺼내는 연산 지원
- cf) top(), pop() 나눠서 하는 이유 : 한 번에 꺼낸다면, 크래쉬 발생 가능성이 존재
- 메모리가 부족해서 복사과정에서 익셉션이 발생한다면, 자료구조가 망가짐
- 따라서, 두 단계로 나눔
- 다만, 그런 크래쉬라면 뻗게 두는 게 나을 것임 (익셉션처리가 아닌)
- cf) top(), pop() 나눠서 하는 이유 : 한 번에 꺼낸다면, 크래쉬 발생 가능성이 존재
전체 코드
#include <mutex>
template<typename T>
class LockStack
{
public:
LockStack() {}
LockStack(const LockStack&) = delete;
LockStack& operator=(const LockStack&) = delete;
void Push(T value)
{
lock_guard<mutex> lock(_mutex);
_stack.push(std::move(value));
}
bool TryPop(&outValue)
{
lock_guard<mutex> lock(_mutex);
if (_stack.empty())
{
return false;
}
outValue = std::move(_stack.top());
_stack.pop();
return true;
}
private:
stack<T> _stack;
mutex _mutex;
};
아쉬운 점 (busy waiting)
데이터가 없더라도 데이터가 있는지 없는지를 체크할 수 없으니까, TryPop을 무한루프로 처리해야 한다. TryPop의 반환값이 false 임을 통해서 데이터가 없음을 확인하게 되는 것이다.
🔥 데이터가 채워지면 pop 할 수 있도록 하자
condition variable 기반
틀
#include <mutex>
template<typename T>
class LockStack
{
public:
LockStack() {}
LockStack(const LockStack&) = delete;
LockStack& operator=(const LockStack&) = delete;
private:
stack<T> _stack;
mutex _mutex;
condition_variable _condVar;
};
- 멤버변수로 condition_variable을 추가
push 연산
void Push(T value)
{
lock_guard<mutex> lock(_mutex);
_stack.push(std::move(value));
// TODO: 여기서 락 해제하는것이 좋을 것
_condVar.notify_one(); // 대기하고있는 하나를 깨움
}
pop 연산
void WaitPop(T& outValue)
{
unique_lock<mutex> lock(_mutex); // condition variable 은 내부적으로 락을 풀었다가 막(?) 하기 때문에, lock_guard가 아닌 unique_lock 사용.
_condVar.wait(lock, [this] { return _stack.empty() == false; }); // 1. 인수로 unique_lock을 받음을 확인할 수 있음 2. predicate로 조건(데이터가 존재할때까지)
// 락을 잡아서 들어갔다가, 조건을 체크해서
// 만약 조건을 만족하지 않으면
// 락을 풀어주고, 잠든다.
// push연산 수행되면 (시그널(notify_one)의 발생), 일어나서 락을 잡고 다시 실행됨
outValue = std::move(_stack.top());
_stack.pop();
}
전체코드
#include <mutex>
template<typename T>
class LockStack
{
public:
LockStack() {}
LockStack(const LockStack&) = delete;
LockStack& operator=(const LockStack&) = delete;
void Push(T value)
{
lock_guard<mutex> lock(_mutex);
_stack.push(std::move(value));
// TODO: 여기서 락 해제하는것이 좋을 것
_condVar.notify_one(); // 대기하고있는 하나를 깨움
}
bool TryPop(&outValue)
{
lock_guard<mutex> lock(_mutex);
if (_stack.empty())
{
return false;
}
outValue = std::move(_stack.top());
_stack.pop();
return true;
}
void WaitPop(T& outValue)
{
unique_lock<mutex> lock(_mutex); // condition variable 은 내부적으로 락을 풀었다가 막(?) 하기 때문에, lock_guard가 아닌 unique_lock 사용.
_condVar.wait(lock, [this] { return _stack.empty() == false; }); // 1. 인수로 unique_lock을 받음을 확인할 수 있음 2. predicate로 조건(데이터가 존재할때까지)
// 락을 잡아서 들어갔다가, 조건을 체크해서
// 만약 조건을 만족하지 않으면
// 락을 풀어주고, 잠든다.
// push연산 수행되면 (시그널(notify_one)의 발생), 일어나서 락을 잡고 다시 실행됨
outValue = std::move(_stack.top());
_stack.pop();
}
private:
stack<T> _stack;
mutex _mutex;
condition_variable _condVar;
};
이로써, busy waiting 문제를 해결했다.
'C・C++ > 멀티스레드' 카테고리의 다른 글
[C++] 멀티 스레드로 소수 구하기 (0) | 2024.10.05 |
---|---|
[C++] Reader-Writer Lock 구현 (0) | 2024.10.05 |
[C++] Thread Local Storage (aka. TLS) (0) | 2024.10.04 |
[C++] Future (3) | 2024.10.04 |
[C++] 멀티스레드 경쟁과 생상자-소비자 문제: 스핀락, 슬립락 구현 / 이벤트, condition_variable (2) | 2024.10.04 |
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- 논문추천
- 톰캣11
- S1
- 이진탐색
- Dispatcher Servlet
- 연관관계 편의 메서드
- 백준
- core c++
- condition variable
- servlet
- 엔티티 설계 주의점
- PS
- Spring MVC
- C
- OOP
- Memory
- reader-writer lock
- pocu
- CPU
- generic swap
- generic sort
- sleep lock
- tomcat11
- S4
- JPA
- thread
- 객체 변조 방어
- Java
- 개발 공부 자료
- tree
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
글 보관함