# moodycamel::ConcurrentQueue: a lock-free MPMC queue in one C++ header

> A single-header queue for passing values between any number of threads, written with the trade-offs spelled out by the author instead of hidden behind an API reference.

**cameron314/concurrentqueue** — A fast multi-producer, multi-consumer lock-free concurrent queue for C++11

- Repository: https://github.com/cameron314/concurrentqueue
- Stars: 12,528 · Forks: 1,943
- Language: C++
- License: NOASSERTION
- Published: 2026-10-06 · Updated: 2026-10-06 · Language: en
- Canonical page: https://hysenlabs.com/projects/cameron314-concurrentqueue

## One header, standard C++11, no assembly

The entire implementation lives in `concurrentqueue.h`. There is nothing to build and nothing to link, which is the first thing that matters for a data structure people drop into existing C++ codebases. The author lists portability as a feature: no assembly anywhere, everything built from standard C++11 primitives, and the algorithm implementations themselves are platform independent.

There is a compiler floor, and it is stated plainly. You need something reasonably recent, VS2012 or newer or g++ 4.8, because the code leans on specific language and library behaviour. The README specifically calls out g++ 4.6 as unsupported due to a known `std::atomic` bug, which is the kind of detail that saves an afternoon.

Building the repository itself, for the benchmarks and tests rather than for use, is an ordinary CMake project. The tree holds `CMakeLists.txt`, a `build/` directory, `benchmarks/`, `tests/` and `concurrentqueueConfig.cmake.in`, the last being a package config file so a parent project can find it through `find_package`.

```bash
git clone https://github.com/cameron314/concurrentqueue.git
cd concurrentqueue
cmake .
make
```

## Why the author wrote it, and what came before

The README spends more space on what this queue is not for than most projects spend on features. The stated motivation is a gap in the C++ ecosystem: there were academic papers with lock-free queue implementations, but usable source was hard to find and tests were harder. Two production-grade options existed and both had restrictions. Boost's lock-free queue was limited to objects with trivial assignment operators and trivial destructors. Intel's TBB queue was not lock-free at all and also required trivial constructors.

That constraint is worth dwelling on, because it explains the design. A queue that only accepts trivially constructible, trivially destructible objects can move elements with cheap bit copies and reuse nodes without running user code. This one is templated and moves elements where it can, which means it can hold a `std::string`, a `std::vector`, or a type with an expensive constructor, at the cost of not being able to do some of the pointer-level tricks that the restricted versions get to use.

The first line of the feature list is blunt about performance, describing the queue as having blazing fast performance and linking to a separate benchmarks page. Read that as a claim to check rather than a settled fact, since benchmark methodology is not something the repository README explains.

There is also a sibling project. The note at the top of the README points at a separate single-producer single-consumer queue for anyone who does not need multi-producer machinery, which is usually the right call when only one thread writes.

## Contiguous blocks and a sub-queue per producer

The high-level design section is where the real argument sits. Elements are stored in contiguous blocks rather than a linked list, which the author gives as the reason for the performance. The queue is then a collection of sub-queues, one per producer, and a consumer walks the collection checking each sub-queue until it finds one that is not empty. From the outside this is invisible: you call `enqueue` and `try_dequeue` and do not deal with any of it.

The interesting consequence is the one the README admits is non-intuitive. If two producers enqueue at the same moment, there is no defined order between those elements when they come out. The author argues this is normally fine, because with a fully linearizable queue there would still be a race between the producer threads and you could not rely on the order anyway.

The case where it bites is when you add your own synchronization between two producers. Once you impose a total order yourself, you would reasonably expect the queue to preserve it, and this queue does not promise that. The suggested fix is to stop treating yourself as two producers: create one producer token and use it from both threads, synchronizing access to that token. The author is candid that this is not free, but points out that the extra synchronization was already there.

Ordering within any single producer's stream is preserved. That is the guarantee you can rely on when you structure your code as one producer token per thread.

## Not linearizable, not sequentially consistent, and that is the point

Most lock-free containers hide their memory ordering story. This one devotes a whole section to it. The queue is not sequentially consistent: there is a happens-before relationship between an element going in and the same element coming out, but anything beyond that, including a loop that drains the queue until it appears empty, requires the caller to think about explicit memory ordering to get the behaviour they want. The README's own summary of that situation is that it can sometimes be difficult to use the queue correctly, and its advice is to follow the samples.

The upside is named in the same breath. Dropping sequential consistency is what buys the throughput. That is a straight trade, offered rather than disguised, and it is the single most important thing to understand before adopting the queue.

The other stated non-goal is linearizability, covered above and rooted in the same assumption that producers are independent. A third limitation is NUMA awareness: the queue re-uses memory aggressively internally, which the author expects will not scale well across NUMA nodes. The author is also honest that no other lock-free queue he knows of is NUMA aware either, with SALSA named as the exception but noted as having no publicly available implementation.

There is a version of this project for people who would rather not think about any of it, covered next.

## Bulk operations and the blocking wrapper

The API is the one you would expect from a templated queue, with the difference that it is safe to call from many threads at once. Enqueue returns a `ProducerToken` optionally, `try_dequeue` and `try_dequeue_bulk` come in pairs with bulk variants, and the constructor takes an optional size estimate so the queue can allocate once up front instead of growing under load. Elements are moved rather than copied where the type allows it.

```cpp
moodycamel::ConcurrentQueue<int> q;
q.enqueue(25);

int item;
bool found = q.try_dequeue(item);
assert(found && item == 25);
```

Bulk operations are the feature the author argues distinguishes this queue from the alternatives. Enqueueing or dequeueing a batch at a time is much faster than one element at a time, and the README claims the design approaches and sometimes exceeds the speed of a non-concurrent queue even under heavy contention. Whether that holds for your element type and core count is a question for the benchmark suite rather than for this page.

There is also `BlockingConcurrentQueue`, in its own `blockingconcurrentqueue.h`, which depends on `concurrentqueue.h` and on `lightweightsemaphore.h`. That last file is a compact semaphore implementation used internally for the blocking wait. The blocking variant is the right choice when a consumer should sleep until work arrives instead of spinning on a failed `try_dequeue`.

The exception safety claim is on the feature list, and the C API contributed in v1.0.3 means the same structure is reachable from code that cannot use templates.

## A release history that barely moves

Only three tags have ever shipped, and the gaps between them say more than any of the notes. v1.0.3 landed on 2021-02-24 with three items: a workaround for a GCC 9.2 compile-time bug, a basic C API contributed by someone else, and a basic `CMakeLists.txt` also contributed. v1.0.4 came on 2023-06-19 to re-enable implicit producer reuse after races in previously disabled code were fixed, plus minor tweaks. v1.0.5 arrived on 2026-04-03 with the entire note reading minor maintenance.

That is a project whose API has settled. The version numbers barely moved for a decade, which is the correct shape for a data structure: you want to be able to vendor a header and know it will keep compiling. The `internal/` directory in the tree suggests the header is split during development and concatenated for distribution, which is how a single-header library that is also a real codebase usually works.

The repository carries 12,492 stars, 1,933 forks and 77 open issues, with the last push dated 2026-07-11 and no archive flag. The README also has a `samples.md` at the root and points to the author's blog for an overview of the internal design and a separate write-up for the full details. That is the right reading order for anyone about to depend on this: the README tells you what the guarantees are, the samples show you how to live with them, and the design posts explain why they are shaped that way.

## Conclusion

This queue is genuinely good at one thing: moving objects between threads cheaply, in bulk, without a mutex and without an assembly blob in your build. The README is unusually candid that it is not linearizable, not sequentially consistent and not NUMA aware, and that candour is the reason to trust the rest of it. Copy `concurrentqueue.h` into a project, use one producer token per thread, and follow `samples.md` rather than improvising around the memory ordering. The last push was 2026-07-11 and the newest tag, v1.0.5, shipped on 2026-04-03 with the note that it is minor maintenance, so the code you read is the code people have been shipping for over a decade.

## FAQ

### Is moodycamel::ConcurrentQueue linearizable?

No, and the README says so directly. Its design assumes producers are independent, so two producers enqueueing at the same time have no defined order between their elements. If you need a total order across producers, create a single producer token and enqueue through it from both threads while synchronizing access to that token yourself.

### How do I add ConcurrentQueue to my C++ project?

Copy `concurrentqueue.h` into your project and include it. There is nothing to compile and nothing to link. The blocking variant lives in `blockingconcurrentqueue.h` and also needs `lightweightsemaphore.h` next to it.

### Which compilers does ConcurrentQueue support?

The implementation uses key C++11 features, so you need a reasonably recent compiler such as VS2012 or newer, or g++ 4.8. The README specifically notes that g++ 4.6 is not supported because of a known `std::atomic` bug, and that the algorithms themselves are platform independent.

### Does ConcurrentQueue scale on NUMA machines?

The author expects it not to, and says so. The queue re-uses memory heavily internally, which works against NUMA architectures. He adds that he is not aware of another lock-free queue that is NUMA aware, aside from SALSA, which has no publicly available implementation.

## Sources

- [cameron314/concurrentqueue on GitHub](https://github.com/cameron314/concurrentqueue)
- [Issues](https://github.com/cameron314/concurrentqueue/issues)
- [README](https://github.com/cameron314/concurrentqueue/blob/master/README.md)
- [Releases](https://github.com/cameron314/concurrentqueue/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/cameron314-concurrentqueue
