0Pricing
C++ Academy · 강의

잠금 없는 큐 구현

단일 생산자·단일 소비자 잠금 없는 큐의 설계를 단계별로 살펴봅니다.

잠금 없는 큐 구현은(는) CoddyKit의 무료 C++ Academy 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 C++ Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. C++ Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

무잠금 큐를 사용하는 이유

뮤텍스를 사용하는 큐는 경합이 심할 때 병목이 될 수 있습니다. 무잠금 큐를 사용하면 생산자와 소비자가 동시에 진행할 수 있습니다.

SPSC와 MPMC

두 가지 방식이 있습니다:

  • SPSC — 단일 생산자, 단일 소비자(가장 단순하고 빠름)
  • MPMC — 여러 생산자, 여러 소비자(가장 일반적임)

양쪽 끝을 모두 제어할 수 있다면 SPSC가 자연스러운 선택입니다.

SPSC 링 버퍼 스케치

두 개의 인덱스를 사용하는 순환 버퍼입니다. 하나는 소비자의 헤드이고 다른 하나는 생산자의 테일입니다. 각 측은 자신의 인덱스만 업데이트합니다.

template <typename T, size_t N>
class SpscQueue {
    T buffer_[N];
    std::atomic<size_t> head_{0};
    std::atomic<size_t> tail_{0};
public:
    bool push(const T& v);
    bool pop(T& v);
};

SPSC 삽입

생산자는 빈 슬롯을 확인하고 데이터를 쓴 다음, 테일을 업데이트하여 게시합니다.

bool push(const T& v) {
    const size_t t = tail_.load(std::memory_order_relaxed);
    const size_t next = (t + 1) % N;
    if (next == head_.load(std::memory_order_acquire))
        return false;     // full
    buffer_[t] = v;
    tail_.store(next, std::memory_order_release);
    return true;
}

SPSC 추출

소비자는 데이터가 있는지 확인하고 읽은 다음, 헤드를 업데이트하여 게시합니다.

bool pop(T& v) {
    const size_t h = head_.load(std::memory_order_relaxed);
    if (h == tail_.load(std::memory_order_acquire))
        return false;     // empty
    v = buffer_[h];
    head_.store((h + 1) % N, std::memory_order_release);
    return true;
}

메모리 순서 조합

테일에 대한 릴리스 저장은 소비자의 테일에 대한 어커이즈 로드와 동기화됩니다. 반대 방향도 마찬가지입니다. 올바른 순서 지정을 사용하지 않으면 데이터 쓰기가 인덱스 업데이트 이후로 재배치될 수 있습니다.

캐시 라인 패딩

거짓 공유를 피하려면 head_와 tail_을 별도의 캐시 라인에 배치하십시오. 일반적으로 두 캐시 라인을 64바이트 간격으로 둡니다. alignas를 사용하십시오.

alignas(64) std::atomic<size_t> head_{0};
alignas(64) std::atomic<size_t> tail_{0};

MPMC: 훨씬 더 어렵습니다

여러 생산자 또는 소비자를 지원하려면 추가적인 조정이 필요하며, 대개 공유 인덱스에 CAS 루프를 사용합니다. 다양한 설계가 존재합니다(Vyukov 큐, MS-queue, Hazard-Pointer 기반 방식 등).

Boost.Lockfree

제품 수준의 무잠금 큐를 구현하기는 어렵습니다. 직접 구현하기보다 Boost.Lockfree 또는 Folly의 ProducerConsumerQueue를 사용하십시오.

장단점

무잠금 큐의 장단점은 다음과 같습니다.

  • 경쟁이 발생할 때 더 높은 처리량
  • 제한된 지연 시간(잠금을 기다리지 않음)
  • 작성하고 디버깅하기가 훨씬 어려움
  • 메모리 순서 지정 오류가 조용히 발생하여 찾아내기 어려움

무잠금 코드 테스트

데이터 경쟁을 찾아내려면 ThreadSanitizer(-fsanitize=thread)를 사용하십시오. 순서 지정 오류를 드러내려면 무작위 대기를 삽입하는 스트레스 테스트를 사용하십시오.

뮤텍스로 충분한 경우

대부분의 애플리케이션에는 무잠금 큐가 필요하지 않습니다. 먼저 측정하십시오. 뮤텍스로 보호되는 잘 구현된 큐는 일괄 처리와 함께 사용할 때 특히 충분한 성능을 내는 경우가 많습니다.

간단히 확인하기

거짓 공유란 무엇이며, head_와 tail_에 패딩을 추가하는 이유는 무엇입니까?

요약

무잠금 SPSC 큐는 생산자가 소유하는 테일과 소비자가 소유하는 헤드를 사용하는 링 버퍼입니다. 어커이즈/릴리스 순서 지정을 사용하고, 인덱스에 패딩을 추가하여 서로 다른 캐시 라인에 배치하십시오. MPMC에는 검증된 라이브러리를 사용하는 것이 좋습니다.

자주 묻는 질문

“잠금 없는 큐 구현” 강의는 무료인가요?

네 — “잠금 없는 큐 구현” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 C++ Academy 강의 전체를 잠금 해제할 수 있습니다. C++ Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“잠금 없는 큐 구현”에서 뭘 배우나요?

단일 생산자·단일 소비자 잠금 없는 큐의 설계를 단계별로 살펴봅니다. 브라우저에서 직접 실행하는 실습 코드로 C++ Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

C++ Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 C++ Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.

“잠금 없는 큐 구현” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 C++ Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 C++ Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. std::atomic과 메모리 순서
  2. 비교 및 교환 CAS 패턴
  3. 잠금 없는 큐 구현
  4. 위험 포인터와 ABA 문제
← C++ Academy(으)로 돌아가기