잠금 없는 큐 구현
단일 생산자·단일 소비자 잠금 없는 큐의 설계를 단계별로 살펴봅니다.
잠금 없는 큐 구현은(는) 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 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.