본문 바로가기
멀티쓰레드프로그래밍

Lock-Base stack, queue

by kcj3054 2022. 3. 30.

락기반의 스택, 큐

  • 일반적인 스택 큐를 사용할때랑 멀티스레드 프로그래밍에서 스택 큐를 사용때는 조금 다르다..

  • empty체크에서 1개가 남아있을 경우 비어있지 않다고 말해줄 수 있을텐데 만약 그순간에 다른 스레드가 pop을 하면 이것은 비어있는 현상이 된다..

락기반 큐 헤더 부분

#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));
        _condVar.notify_one();
    }

    //TryPop 대체용
    void WaitPop(T& value)
    {
        unique_lock<mutex> lock(_mutex);
        _condVar.wait(lock, [this] {return _queue.empty() == false; });  // 조건에 맞을때까지 wait..
        value = std::move(_queue.front());
        _queue.pop();
    }

    //데이터가 없더라도 데이가 있는지 없는지 체크를 해야서 계속해서 TryPop을 해야한다 SPINLOCK 느낌..
    bool TryPop(T& value)
    {
        lock_guard<mutex> lock(_mutex);
        if (_queue.empty()) return false;

        value = std::move(_queue.front());
        _queue.pop();
        return true;
    }
public:
    queue<T> _queue;
    mutex _mutex;
    condition_variable _condVar;
};

  • 이 소스에서 TryPop과 WaitPop의 차이를 보자. !

  • TryPop에서는 empty인지 계속해서 체크한다 그런데 계속 데이터를 넣지 않고 있을 경우 이 체크는 무의미한 것이다. 이것은 spinlock과 비슷하네..

  • 그래서 TryPop의 이러한 무의미한 반복을 막기 위해서 WaitPop이 생겼다. WaitPop은 condition_variable을 통해 조건을 줘서 조건이 맞을때까지 wait를 하도록 시켜서 무의미하게 체크를 하지않아도 된다. 여기서 unique_lock을 쓴 이유는 상태변수를 사용하기 위해서이다.

TryPop의 주소 value부분

bool TryPop(T& value)
    {
        lock_guard<mutex> lock(_mutex);
        if (_stack.empty()) return false;

        value = std::move(_stack.top());
        _stack.pop();
        return true;
    }
  • 일반적인 pop에서는 매개변수 인자가 필요가 없다 그렇지만 여기서는 value를 넣었는데 이유가 멀까 싶었는데 main부분에서 data를 꺼내오기 위함이었다 그래서 받아올때도 &로 받아오면서 데이터를 넣는다.

락기반 스택 부분

  • 큐 부분과 동일하다.
#pragma once
#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)); //이동 성능 향상 
        _cv.notify_one();
    }

    //TryPop 대체용
    void WaitPop(T& value)
    {
        unique_lock<mutex> lock(_mutex);
        _cv.wait(lock, [this] {return _stack.empty() == false; });  // 조건에 맞을때까지 wait..
        value = std::move(_stack.top());
        _stack.pop();
    }

    //데이터가 없더라도 데이가 있는지 없는지 체크를 해야서 계속해서 TryPop을 해야한다 SPINLOCK 느낌..
    bool TryPop(T& value)
    {
        lock_guard<mutex> lock(_mutex);
        if (_stack.empty()) return false;

        value = std::move(_stack.top());
        _stack.pop();
        return true;
    }
    /*
    * 멀티스레드 환경에서는 엠피가 큰 효과가 없다 이유는 현재 엠티가 아니라고 체크 한후 pop을 할려고했는데
    * 그 사이에 다른 스레드가  와서 나머지를 가져간다면 엠티가 되기때문...
    */
    bool Empty()
    {
        lock_guard<mutex> lock(_mutex);

    }
private:
    stack<T> _stack;
    mutex _mutex;
    condition_variable _cv;
};

  • 루키스님의 서버강의를 수강 후 학습하여 작성하였습니다.

'멀티쓰레드프로그래밍' 카테고리의 다른 글

Future  (0) 2022.04.07
cpu 파이프라인  (0) 2022.04.07
캐시, 메모리 계층  (0) 2022.04.06
TLS (ThreadLocalStorage)  (0) 2022.04.06
메모리 모델  (0) 2022.03.30