🕷️ Crawler Inspector

URL Lookup

Direct Parameter Lookup

Raw Queries and Responses

1. Shard Calculation

Query:
Response:
Calculated Shard: 6 (from laksa157)

2. Crawled Status Check

Query:
Response:

3. Robots.txt Check

Query:
Response:

4. Spam/Ban Check

Query:
Response:

5. Seen Status Check

ℹ️ Skipped - page is already crawled

đź“„
INDEXABLE
âś…
CRAWLED
4 days ago
🤖
ROBOTS ALLOWED

Page Info Filters

FilterStatusConditionDetails
HTTP statusPASSdownload_http_code = 200HTTP 200
Age cutoffPASSdownload_stamp > now() - 6 MONTH0.1 months ago
History dropPASSisNull(history_drop_reason)No drop reason
Spam/banPASSfh_dont_index != 1 AND ml_spam_score = 0ml_spam_score=0
CanonicalPASSmeta_canonical IS NULL OR = '' OR = src_unparsedNot set

Page Details

PropertyValue
URLhttps://codetrips.com/2020/07/26/modern-c-writing-a-thread-safe-queue/
Last Crawled2026-04-04 20:46:16 (4 days ago)
First Indexed2020-12-06 00:55:30 (5 years ago)
HTTP Status Code200
Meta TitleModern C++: Writing a thread-safe Queue – Code Trips & Tips
Meta DescriptionThe STL provides a high-performance queue class in std::queue<T>; however, the API is not very intuitive, or easy, to use and it is not safe to access from multiple threads.In this article, we will show how to wrap it in a more convenient, and thread-safe, API. The C++ Standard Template Library (STL) offers a template-class…
Meta Canonicalnull
Boilerpipe Text
The STL provides a high-performance queue class in std::queue<T> ; however, the API is not very intuitive, or easy, to use and it is not safe to access from multiple threads. In this article, we will show how to wrap it in a more convenient, and thread-safe, API. The C++ Standard Template Library (STL) offers a template-class std::queue<T> that implements Queue semantics: the usual push / pop / empty methods to implement a FIFO queue for elements of type T . The class also offers the ability to specify the underlying Container to use for storage at construction ( std::deque<T> is used by default). The requirements on the T class of queued elements are pretty light, the most obvious being that they must be “copyable” (although, if the class to be contained cannot be copied, one can use either references of pointers – with the usual caveat that it becomes a bit trickier to ensure they don’t go out of scope while still being referenced to from the queue). The design (and implementation) of the API is meant for performance and small memory footprint; however, it makes for a somewhat awkward usage; most surprising is that pop() will not return the T element at the front of the queue, if any. One has to use front() to actually get it, then pop() to remove; further, if there is no such element (i.e., the queue is empty) calling pop() may result in undefined behavior, depending from the underlying Container: in fact, the default std::deque will cause such undefined behavior : Calling front on an empty container is undefined. Further, access to the elements of the queue is not thread-safe; so implementing the classical Producer/Consumer pattern, with multiple “worker” threads would result in chaos and almost certainly undefined behavior (that would actually be “best case scenario”: at least one would know that things have gone horribly off the rails; the alternative, silent incorrect computation, is way worse – especially when it comes to distributed systems in Production). This brief technical note shows first how to build a simple, yet effective, single-threaded Queue<T> class, with possibly slightly less efficient performance, but more intuitive API; and then how to extend it so that it is thread-safe. Source code All the source is available on BitBucket and can be cloned on your machine via: git clone git@bitbucket.org:marco/samples.git All my code uses CMake to build, and I warmly recommend the use of clang++ on both Linux and MacOS; however the code does not use any external library or facility, and should compile/run anywhere, so long as your compiler supports C++17. A better queue The first iteration of the Queue<T> class is available at the thread-unsafe tag: git checkout thread-unsafe A simplified listing for the class is shown here: template<typename T> class Queue { std::queue<T> queue_; public: Queue() = default; Queue(const Queue<T> &) = delete ; Queue& operator=(const Queue<T> &) = delete ; Queue(Queue<T>&& other) { queue_ = std::move(other.queue_); } virtual ~Queue() { } bool empty() const { return queue_.empty(); } unsigned long size() const { return queue_.size(); } std::optional<T> pop() { if (queue_.empty()) { return {}; } T tmp = queue_.front(); queue_.pop(); return tmp; } void push(const T &item) { queue_.push(item); } std::optional<T> peek() { if (queue_.empty()) { return {}; } return queue_.front(); } }; As you can see, the copy and assignment constructors have been deleted, and we have implemented the move constructor, as it may be useful for certain use cases, when it is necessary to pass around the Queue’s ownership. In this implementation, pop() does return the element at the front of the queue, if any, or an empty optional otherwise. The std::optional<T> class has been added to C++17 (at long last, one might add) and finally brings to C++ a concept that has been part of Scala since its inception, and Java since JDK 8 (although a bit clumsy in its first iteration, and we had to wait until JDK 10 for a better API). An optional value is a “better alternative” to returning a nullptr (for pointer types) or a sentinel value for value types (how many types did you return -1 to signal “eeehh, I don’t know”): it essentially signals to the caller that what they asked for is not there, and that is neither an error nor an aberration. An optional<T> behaves as expected when converted to a boolean in an if clause, and uses the * indirection operator to dereference its value (if any); the typical pattern is: Queue<MyData> queue; // use the queue... auto elem_maybe = queue.pop(); if (elem_maybe) { MyData elem = *elem_maybe; // use elem... } When used by a single thread (and, beware, this class is not thread-safe), one can also “guard” via the use of empty() and bypass the check: if (!queue.empty()) { // We know that the optional is non-empty here auto elem = *queue.pop(); } It no work so good As far as Queues go, and provided this is used by only one thread at a time, this is a decent implementation of a Queue, and will work just fine, with only limited overhead with respect to the (better, but slightly awkward) std::queue . However, when used by multiple threads (as demonstrated by the main() method in the sample code), weird and wonderful things will happen, none of them good. By tweaking the values in the sleep duration calls, one can go from perfectly functioning code, to slightly baffling results (such as only 29 results being processed) to outright segfaults. A thread-safe Queue Given that the most common use case for a Queue is to decouple worker threads in the Producer/Consumer design pattern, it is worth considering how to improve the design of the Queue<T> class so that multi-threaded clients can use it. The code here is tagged at thread-safe : git checkout thread-safe Interestingly enough, we can leave the API almost unchanged, and we only need to add a std::mutex to guard access to the “wrapped” std::queue : template<typename T> class ThreadsafeQueue { std::queue<T> queue_; mutable std::mutex mutex_; // Moved out of public interface to prevent races between this // and pop(). bool empty() const { return queue_.empty(); } public: ThreadsafeQueue() = default; ThreadsafeQueue(const ThreadsafeQueue<T> &) = delete ; ThreadsafeQueue& operator=(const ThreadsafeQueue<T> &) = delete ; ThreadsafeQueue(ThreadsafeQueue<T>&& other) { std::lock_guard<std::mutex> lock(mutex_); queue_ = std::move(other.queue_); } virtual ~ThreadsafeQueue() { } unsigned long size() const { std::lock_guard<std::mutex> lock(mutex_); return queue_.size(); } std::optional<T> pop() { std::lock_guard<std::mutex> lock(mutex_); if (queue_.empty()) { return {}; } T tmp = queue_.front(); queue_.pop(); return tmp; } void push(const T &item) { std::lock_guard<std::mutex> lock(mutex_); queue_.push(item); } }; The most obvious change is the addition of a std::mutex to guard all accesses to the underlying std::queue , using the std:lock_guard pattern, to ensure that the lock is released by the holding thread under all possible exit scenarios (including exception being raised). A more subtle API change is the removal of the empty() method from public access: now clients can no longer query the Queue about its state, but can only infer its “emptiness state” by attempting to pop an element and obtaining an empty optional . The reason for this is that under no circumstances the Queue can guarantee that matters won’t change between the time the client queries empty() and the time pop() is called, making the point entirely moot, and this code a potential source of intermittent (read: hard to pin) bugs: if (!queue.empty()) { // What could possibly go wrong? A lot, it turns out. auto elem = *queue.pop(); } Obviously, there is a perfectly legitimate use case for the use of empty() as a simple “informational” (and, necessarily, ephemeral) assessment of the state of the Queue, but personally I just prefer to take the gun away from the child, as it would require (a) adding in large, bold font a warning to the user and (b) relying on the user to actually heed that warning. This is a more general approach to API design that holds, loosely: “Make interfaces easy to use right, and hard to use wrong” (originally attributed to Martin Fowler). About Locking and Performance There is a never-ending debate as to whether locks (and multi-threading in general) are pure evil and should be avoided at all costs; in particular, many folks feel that the gain to be had by using multi-core architectures is usually lost to the complexity and overhead to manage context switching, and locks acquisition (and release). The answer, as so many things in distributed systems, is “it depends”; and, most certainly it is foolish (if not outright fraudulent) to make statements about performance and optimizations without hard data and objective measurements; especially in view of the overall system’s stated goals and priorities. Be that as it may, if you believe that adopting a multi-threaded architecture would be beneficial to your system, and you have measured that using mutually exclusive locks (that’s actually what “mutex” stands for) does not negatively impact performance, this is a relatively efficient and simple-to-use implementation of a thread-safe queue.
Markdown
[Skip to content](https://codetrips.com/2020/07/26/modern-c-writing-a-thread-safe-queue/#wp--skip-link--target) - [Facebook](https://codetrips.com/2020/07/26/modern-c-writing-a-thread-safe-queue/) - [Twitter](https://codetrips.com/2020/07/26/modern-c-writing-a-thread-safe-queue/) - [Instagram](https://codetrips.com/2020/07/26/modern-c-writing-a-thread-safe-queue/) - [TikTok](https://codetrips.com/2020/07/26/modern-c-writing-a-thread-safe-queue/) - [YouTube](https://codetrips.com/2020/07/26/modern-c-writing-a-thread-safe-queue/) [![Code Trips & Tips](https://codetrips.com/wp-content/uploads/2022/04/cropped-alertavert-logo.png)](https://codetrips.com/) # [Code Trips & Tips](https://codetrips.com/) - - [About](https://codetrips.com/about/) [Coding](https://codetrips.com/category/coding/) ## Modern C++: Writing a thread-safe Queue The STL provides a high-performance queue class in std::queue\<T\>; however, the API is not very intuitive, or easy, to use and it is not safe to access from multiple threads.In this article, we will show how to wrap it in a more convenient, and thread-safe, API. The C++ Standard Template Library (STL) offers a template-class… [Marco](https://codetrips.com/author/massenz/) July 26, 2020 6–9 minutes > The STL provides a high-performance queue class in `std::queue<T>`; however, the API is not very intuitive, or easy, to use and it is not safe to access from multiple threads. > In this article, we will show how to wrap it in a more convenient, and thread-safe, API. ![](https://codetrips.com/wp-content/uploads/2020/07/java_01.jpg?w=1024) The C++ Standard Template Library (STL) offers a [template-class `std::queue<T>`](https://en.cppreference.com/w/cpp/container/queue) that implements Queue semantics: the usual `push`/`pop`/`empty` methods to implement a FIFO queue for elements of type `T`. The class also offers the ability to specify the underlying Container to use for storage at construction ([`std::deque<T>`](https://en.cppreference.com/w/cpp/container/deque) is used by default). The requirements on the `T` class of queued elements are pretty light, the most obvious being that they must be “copyable” (although, if the class to be contained cannot be copied, one can use either references of pointers – with the usual caveat that it becomes a bit trickier to ensure they don’t go out of scope while still being referenced to from the queue). The design (and implementation) of the API is meant for performance and small memory footprint; however, it makes for a somewhat awkward usage; most surprising is that `pop()` will not return the `T` element at the front of the queue, if any. One has to use `front()` to actually get it, then `pop()` to remove; further, if there is no such element (i.e., the queue is empty) calling `pop()` *may* result in undefined behavior, depending from the underlying Container: in fact, [the default `std::deque` will cause such undefined behavior](https://en.cppreference.com/w/cpp/container/deque/front): > `Calling front on an empty container is undefined.` Further, access to the elements of the queue is not thread-safe; so implementing the classical Producer/Consumer pattern, with multiple “worker” threads would result in chaos and almost certainly undefined behavior (that would actually be “best case scenario”: at least one would know that things have gone horribly off the rails; the alternative, silent incorrect computation, is way worse – especially when it comes to distributed systems in Production). This brief technical note shows first how to build a simple, yet effective, single-threaded `Queue<T>` class, with possibly slightly less efficient performance, but more intuitive API; and then how to extend it so that it is thread-safe. ## Source code All the source is [available on BitBucket](https://bitbucket.org/marco/samples) and can be cloned on your machine via: ``` git clone git@bitbucket.org:marco/samples.git ``` All my code uses [CMake](http://cmake.org/) to build, and I warmly recommend the use of `clang++` on both Linux and MacOS; however the code does not use any external library or facility, and should compile/run anywhere, so long as your compiler supports C++17. # A better queue The first iteration of the `Queue<T>` class is available at the `thread-unsafe` tag: ``` git checkout thread-unsafe ``` A simplified listing for the class is shown here: ``` template<typename T> class Queue { std::queue<T> queue_; public: Queue() = default; Queue(const Queue<T> &) = delete ; Queue& operator=(const Queue<T> &) = delete ; Queue(Queue<T>&& other) { queue_ = std::move(other.queue_); } virtual ~Queue() { } bool empty() const { return queue_.empty(); } unsigned long size() const { return queue_.size(); } std::optional<T> pop() { if (queue_.empty()) { return {}; } T tmp = queue_.front(); queue_.pop(); return tmp; } void push(const T &item) { queue_.push(item); } std::optional<T> peek() { if (queue_.empty()) { return {}; } return queue_.front(); } }; ``` As you can see, the copy and assignment constructors have been deleted, and we have implemented the move constructor, as it may be useful for certain use cases, when it is necessary to pass around the Queue’s ownership. In this implementation, `pop()` does return the element at the front of the queue, if any, or an empty `optional` otherwise. The `std::optional<T>` class has been added to C++17 (at long last, one might add) and finally brings to C++ a concept that has been part of Scala since its inception, and Java since JDK 8 (although a bit clumsy in its first iteration, and we had to wait until JDK 10 for a better API). An `optional` value is a “better alternative” to returning a `nullptr` (for pointer types) or a `sentinel value` for value types (how many types did you return `-1` to signal “eeehh, I don’t know”): it essentially signals to the caller that what they asked for is not there, and that is neither an error nor an aberration. An `optional<T>` behaves as expected when converted to a `boolean` in an `if` clause, and uses the `*` indirection operator to dereference its value (if any); the typical pattern is: ``` Queue<MyData> queue; // use the queue... auto elem_maybe = queue.pop(); if (elem_maybe) { MyData elem = *elem_maybe; // use elem... } ``` When used by a single thread (and, beware, this class is **not** thread-safe), one can also “guard” via the use of `empty()` and bypass the check: ``` if (!queue.empty()) { // We know that the optional is non-empty here auto elem = *queue.pop(); } ``` ## It no work so good As far as Queues go, and provided this is used by only one thread at a time, this is a decent implementation of a Queue, and will work just fine, with only limited overhead with respect to the (better, but slightly awkward) `std::queue`. However, when used by multiple threads (as demonstrated by the `main()` method in the sample code), weird and wonderful things will happen, none of them good. By tweaking the values in the `sleep` duration calls, one can go from perfectly functioning code, to slightly baffling results (such as only 29 results being processed) to outright segfaults. # A thread-safe Queue Given that the most common use case for a Queue is to decouple worker threads in the Producer/Consumer design pattern, it is worth considering how to improve the design of the `Queue<T>` class so that multi-threaded clients can use it. The code here is tagged at `thread-safe`: ``` git checkout thread-safe ``` Interestingly enough, we can leave the API almost unchanged, and we only need to add a `std::mutex` to guard access to the “wrapped” `std::queue`: ``` template<typename T> class ThreadsafeQueue { std::queue<T> queue_; mutable std::mutex mutex_; // Moved out of public interface to prevent races between this // and pop(). bool empty() const { return queue_.empty(); } public: ThreadsafeQueue() = default; ThreadsafeQueue(const ThreadsafeQueue<T> &) = delete ; ThreadsafeQueue& operator=(const ThreadsafeQueue<T> &) = delete ; ThreadsafeQueue(ThreadsafeQueue<T>&& other) { std::lock_guard<std::mutex> lock(mutex_); queue_ = std::move(other.queue_); } virtual ~ThreadsafeQueue() { } unsigned long size() const { std::lock_guard<std::mutex> lock(mutex_); return queue_.size(); } std::optional<T> pop() { std::lock_guard<std::mutex> lock(mutex_); if (queue_.empty()) { return {}; } T tmp = queue_.front(); queue_.pop(); return tmp; } void push(const T &item) { std::lock_guard<std::mutex> lock(mutex_); queue_.push(item); } }; ``` The most obvious change is the addition of a `std::mutex` to guard all accesses to the underlying `std::queue`, using the `std:lock_guard` pattern, to ensure that the lock is released by the holding thread under all possible exit scenarios (including exception being raised). A more subtle API change is the removal of the `empty()` method from public access: now clients can no longer query the Queue about its state, but can only infer its “emptiness state” by attempting to `pop` an element and obtaining an empty `optional`. The reason for this is that under no circumstances the Queue can guarantee that matters won’t change between the time the client queries `empty()` and the time `pop()` is called, making the point entirely moot, and this code a potential source of intermittent (read: hard to pin) bugs: ``` if (!queue.empty()) { // What could possibly go wrong? A lot, it turns out. auto elem = *queue.pop(); } ``` Obviously, there is a perfectly legitimate use case for the use of `empty()` as a simple “informational” (and, necessarily, ephemeral) assessment of the state of the Queue, but personally I just prefer to take the gun away from the child, as it would require (a) adding in large, bold font a warning to the user and (b) relying on the user to actually heed that warning. This is a more general approach to API design that holds, loosely: “Make interfaces easy to use right, and hard to use wrong” (originally attributed to Martin Fowler). ## About Locking and Performance There is a never-ending debate as to whether locks (and multi-threading in general) are pure evil and should be avoided at all costs; in particular, many folks feel that the gain to be had by using multi-core architectures is usually lost to the complexity and overhead to manage context switching, and locks acquisition (and release). The answer, as so many things in distributed systems, is “it depends”; and, most certainly it is foolish (if not outright fraudulent) to make statements about performance and optimizations without hard data and objective measurements; especially in view of the overall system’s stated goals and priorities. Be that as it may, if you believe that adopting a multi-threaded architecture would be beneficial to your system, and you have measured that using mutually exclusive locks (that’s actually what “mutex” stands for) does not negatively impact performance, this is a relatively efficient and simple-to-use implementation of a thread-safe queue. ### Share this: - [Email a link to a friend (Opens in new window) Email](mailto:?subject=%5BShared%20Post%5D%20Modern%20C%2B%2B%3A%20Writing%20a%20thread-safe%20Queue&body=https%3A%2F%2Fcodetrips.com%2F2020%2F07%2F26%2Fmodern-c-writing-a-thread-safe-queue%2F&share=email) - [Share on Reddit (Opens in new window) Reddit](https://codetrips.com/2020/07/26/modern-c-writing-a-thread-safe-queue/?share=reddit) - [Share on LinkedIn (Opens in new window) LinkedIn](https://codetrips.com/2020/07/26/modern-c-writing-a-thread-safe-queue/?share=linkedin) - [Share on X (Opens in new window) X](https://codetrips.com/2020/07/26/modern-c-writing-a-thread-safe-queue/?share=twitter) - [Share on Facebook (Opens in new window) Facebook](https://codetrips.com/2020/07/26/modern-c-writing-a-thread-safe-queue/?share=facebook) Like Loading… ### 2 responses to “Modern C++: Writing a thread-safe Queue” 1. ![Ayushdeep Dabas Avatar](https://0.gravatar.com/avatar/c056b746cd8d99218e06d68e5cd03dd36249a5546437fa30d64bb9dbe176febd?s=48&d=identicon&r=G) Ayushdeep Dabas [November 2, 2020 at 6:43 pm](https://codetrips.com/2020/07/26/modern-c-writing-a-thread-safe-queue/comment-page-1/#comment-94104) Thank you so much sir, What could be a good resource for starting with Mordern C++/ Distributed Systems for HFT’s [Reply](https://codetrips.com/2020/07/26/modern-c-writing-a-thread-safe-queue/comment-page-1/?replytocom=94104#respond) 1. ![massenz Avatar](https://2.gravatar.com/avatar/2dc3dfdf7abb156deefebbccb8ff7238bb609210be66e67895a9093cf3b7e76f?s=48&d=identicon&r=G) [massenz](https://codetrips.wordpress.com/) [November 3, 2020 at 12:32 am](https://codetrips.com/2020/07/26/modern-c-writing-a-thread-safe-queue/comment-page-1/#comment-94173) You’re welcome\! I would recommend to start with Scott’s excellent “Effective Modern C++” (<https://amzn.to/34OBo64>) and then learn how to adopt C++17 idioms to use Functional C++ (<https://amzn.to/3jQI1Je>) (full disclosure: I was one of the manuscript reviewers for the latter; a really great book) [Reply](https://codetrips.com/2020/07/26/modern-c-writing-a-thread-safe-queue/comment-page-1/?replytocom=94173#respond) ### Leave a comment [Cancel reply](https://codetrips.com/2020/07/26/modern-c-writing-a-thread-safe-queue/#respond) ### Categories - [AI](https://codetrips.com/category/ai/) (11) - [AWS](https://codetrips.com/category/aws/) (7) - [C++](https://codetrips.com/category/c-3/) (13) - [Cloud](https://codetrips.com/category/cloud/) (13) - [Coding](https://codetrips.com/category/coding/) (85) - [Enterprise](https://codetrips.com/category/enterprise/) (20) - [Go](https://codetrips.com/category/go/) (3) - [Java](https://codetrips.com/category/java/) (15) - [Linux](https://codetrips.com/category/linux/) (15) - [LLM](https://codetrips.com/category/llm/) (8) - [MacOS](https://codetrips.com/category/macos/) (9) - [Management](https://codetrips.com/category/management/) (9) - [Python](https://codetrips.com/category/python/) (28) - [Security](https://codetrips.com/category/security/) (9) - [Shell Scripts](https://codetrips.com/category/shell-scripts/) (18) - [Ubuntu](https://codetrips.com/category/ubuntu/) (25) - [Uncategorized](https://codetrips.com/category/uncategorized/) (10) ## Trending - [![Running Your Own LLM Chat App on Apple Silicon with vLLM-MLX](https://codetrips.com/wp-content/uploads/2026/03/vllm_featured.png?w=1024)](https://codetrips.com/2026/03/30/running-your-own-llm-chat-app-on-apple-silicon-with-vllm-mlx/) [AI](https://codetrips.com/category/ai/), [LLM](https://codetrips.com/category/llm/), [MacOS](https://codetrips.com/category/macos/), [Python](https://codetrips.com/category/python/) ## [Running Your Own LLM Chat App on Apple Silicon with vLLM-MLX](https://codetrips.com/2026/03/30/running-your-own-llm-chat-app-on-apple-silicon-with-vllm-mlx/) [Marco](https://codetrips.com/author/massenz/) - [![When LLM do something impressive, yet fail the common sense test](https://codetrips.com/wp-content/uploads/2025/11/screenshot-2025-11-24-at-23.21.57.png?w=1024)](https://codetrips.com/2025/11/24/when-llm-do-something-impressive-yet-fail-the-common-sense-test/) [AI](https://codetrips.com/category/ai/), [Coding](https://codetrips.com/category/coding/), [LLM](https://codetrips.com/category/llm/) ## [When LLM do something impressive, yet fail the common sense test](https://codetrips.com/2025/11/24/when-llm-do-something-impressive-yet-fail-the-common-sense-test/) [Marco](https://codetrips.com/author/massenz/) - [![Publish a Container to Amazon ECR using GitHub Actions](https://codetrips.com/wp-content/uploads/2025/11/img_2822.jpeg?w=801)](https://codetrips.com/2025/11/09/publish-a-container-to-amazon-ecr-using-github-actions/) [Coding](https://codetrips.com/category/coding/) ## [Publish a Container to Amazon ECR using GitHub Actions](https://codetrips.com/2025/11/09/publish-a-container-to-amazon-ecr-using-github-actions/) [Marco](https://codetrips.com/author/massenz/) - [![Implementing Pooled Embeddings in CUDA Kernel](https://codetrips.com/wp-content/uploads/2025/08/bots-pool.png?w=1024)](https://codetrips.com/2025/08/18/implementing-pooled-embeddings-in-cuda-kernel/) [AI](https://codetrips.com/category/ai/), [C++](https://codetrips.com/category/c-3/), [Coding](https://codetrips.com/category/coding/) ## [Implementing Pooled Embeddings in CUDA Kernel](https://codetrips.com/2025/08/18/implementing-pooled-embeddings-in-cuda-kernel/) [Marco](https://codetrips.com/author/massenz/) Adventures in the quest for ultimate excellence in developing software distributed systems that scale up to millions of nodes and billions of users. - [Facebook](https://codetrips.com/2020/07/26/modern-c-writing-a-thread-safe-queue/) - [Twitter](https://codetrips.com/2020/07/26/modern-c-writing-a-thread-safe-queue/) - [Instagram](https://codetrips.com/2020/07/26/modern-c-writing-a-thread-safe-queue/) - [YouTube](https://codetrips.com/2020/07/26/modern-c-writing-a-thread-safe-queue/) - - [About](https://codetrips.com/about/) [Create a website or blog at WordPress.com](https://wordpress.com/?ref=footer_custom_svg "Create a website or blog at WordPress.com") ## ## Loading Comments... ### - [Comment](https://codetrips.com/2020/07/26/modern-c-writing-a-thread-safe-queue/#comments) - [Reblog](https://codetrips.com/2020/07/26/modern-c-writing-a-thread-safe-queue/) - [Subscribe](https://codetrips.com/2020/07/26/modern-c-writing-a-thread-safe-queue/) [Subscribed](https://codetrips.com/2020/07/26/modern-c-writing-a-thread-safe-queue/) - [![](https://codetrips.com/wp-content/uploads/2023/10/cropped-alertavert-logo.png?w=50) Code Trips & Tips](https://codetrips.com/) - Already have a WordPress.com account? [Log in now.](https://wordpress.com/log-in?redirect_to=https%3A%2F%2Fr-login.wordpress.com%2Fremote-login.php%3Faction%3Dlink%26back%3Dhttps%253A%252F%252Fcodetrips.com%252F2020%252F07%252F26%252Fmodern-c-writing-a-thread-safe-queue%252F) - [Privacy](https://codetrips.com/2020/07/26/modern-c-writing-a-thread-safe-queue/) - - [![](https://codetrips.com/wp-content/uploads/2023/10/cropped-alertavert-logo.png?w=50) Code Trips & Tips](https://codetrips.com/) - [Subscribe](https://codetrips.com/2020/07/26/modern-c-writing-a-thread-safe-queue/) [Subscribed](https://codetrips.com/2020/07/26/modern-c-writing-a-thread-safe-queue/) - [Sign up](https://wordpress.com/start/) - [Log in](https://wordpress.com/log-in?redirect_to=https%3A%2F%2Fr-login.wordpress.com%2Fremote-login.php%3Faction%3Dlink%26back%3Dhttps%253A%252F%252Fcodetrips.com%252F2020%252F07%252F26%252Fmodern-c-writing-a-thread-safe-queue%252F) - [Copy shortlink](https://wp.me/p3vCSa-jC) - [Report this content](https://wordpress.com/abuse/?report_url=https://codetrips.com/2020/07/26/modern-c-writing-a-thread-safe-queue/) - [View post in Reader](https://wordpress.com/reader/blogs/51866606/posts/1216) - [Manage subscriptions](https://subscribe.wordpress.com/) - [Collapse this bar](https://codetrips.com/2020/07/26/modern-c-writing-a-thread-safe-queue/) %d ![](https://pixel.wp.com/b.gif?v=noscript)
Readable Markdown
> The STL provides a high-performance queue class in `std::queue<T>`; however, the API is not very intuitive, or easy, to use and it is not safe to access from multiple threads. > In this article, we will show how to wrap it in a more convenient, and thread-safe, API. ![](https://codetrips.com/wp-content/uploads/2020/07/java_01.jpg?w=1024) The C++ Standard Template Library (STL) offers a [template-class `std::queue<T>`](https://en.cppreference.com/w/cpp/container/queue) that implements Queue semantics: the usual `push`/`pop`/`empty` methods to implement a FIFO queue for elements of type `T`. The class also offers the ability to specify the underlying Container to use for storage at construction ([`std::deque<T>`](https://en.cppreference.com/w/cpp/container/deque) is used by default). The requirements on the `T` class of queued elements are pretty light, the most obvious being that they must be “copyable” (although, if the class to be contained cannot be copied, one can use either references of pointers – with the usual caveat that it becomes a bit trickier to ensure they don’t go out of scope while still being referenced to from the queue). The design (and implementation) of the API is meant for performance and small memory footprint; however, it makes for a somewhat awkward usage; most surprising is that `pop()` will not return the `T` element at the front of the queue, if any. One has to use `front()` to actually get it, then `pop()` to remove; further, if there is no such element (i.e., the queue is empty) calling `pop()` *may* result in undefined behavior, depending from the underlying Container: in fact, [the default `std::deque` will cause such undefined behavior](https://en.cppreference.com/w/cpp/container/deque/front): > `Calling front on an empty container is undefined.` Further, access to the elements of the queue is not thread-safe; so implementing the classical Producer/Consumer pattern, with multiple “worker” threads would result in chaos and almost certainly undefined behavior (that would actually be “best case scenario”: at least one would know that things have gone horribly off the rails; the alternative, silent incorrect computation, is way worse – especially when it comes to distributed systems in Production). This brief technical note shows first how to build a simple, yet effective, single-threaded `Queue<T>` class, with possibly slightly less efficient performance, but more intuitive API; and then how to extend it so that it is thread-safe. ## Source code All the source is [available on BitBucket](https://bitbucket.org/marco/samples) and can be cloned on your machine via: ``` git clone git@bitbucket.org:marco/samples.git ``` All my code uses [CMake](http://cmake.org/) to build, and I warmly recommend the use of `clang++` on both Linux and MacOS; however the code does not use any external library or facility, and should compile/run anywhere, so long as your compiler supports C++17. ## A better queue The first iteration of the `Queue<T>` class is available at the `thread-unsafe` tag: ``` git checkout thread-unsafe ``` A simplified listing for the class is shown here: ``` template<typename T> class Queue { std::queue<T> queue_; public: Queue() = default; Queue(const Queue<T> &) = delete ; Queue& operator=(const Queue<T> &) = delete ; Queue(Queue<T>&& other) { queue_ = std::move(other.queue_); } virtual ~Queue() { } bool empty() const { return queue_.empty(); } unsigned long size() const { return queue_.size(); } std::optional<T> pop() { if (queue_.empty()) { return {}; } T tmp = queue_.front(); queue_.pop(); return tmp; } void push(const T &item) { queue_.push(item); } std::optional<T> peek() { if (queue_.empty()) { return {}; } return queue_.front(); } }; ``` As you can see, the copy and assignment constructors have been deleted, and we have implemented the move constructor, as it may be useful for certain use cases, when it is necessary to pass around the Queue’s ownership. In this implementation, `pop()` does return the element at the front of the queue, if any, or an empty `optional` otherwise. The `std::optional<T>` class has been added to C++17 (at long last, one might add) and finally brings to C++ a concept that has been part of Scala since its inception, and Java since JDK 8 (although a bit clumsy in its first iteration, and we had to wait until JDK 10 for a better API). An `optional` value is a “better alternative” to returning a `nullptr` (for pointer types) or a `sentinel value` for value types (how many types did you return `-1` to signal “eeehh, I don’t know”): it essentially signals to the caller that what they asked for is not there, and that is neither an error nor an aberration. An `optional<T>` behaves as expected when converted to a `boolean` in an `if` clause, and uses the `*` indirection operator to dereference its value (if any); the typical pattern is: ``` Queue<MyData> queue; // use the queue... auto elem_maybe = queue.pop(); if (elem_maybe) { MyData elem = *elem_maybe; // use elem... } ``` When used by a single thread (and, beware, this class is **not** thread-safe), one can also “guard” via the use of `empty()` and bypass the check: ``` if (!queue.empty()) { // We know that the optional is non-empty here auto elem = *queue.pop(); } ``` ## It no work so good As far as Queues go, and provided this is used by only one thread at a time, this is a decent implementation of a Queue, and will work just fine, with only limited overhead with respect to the (better, but slightly awkward) `std::queue`. However, when used by multiple threads (as demonstrated by the `main()` method in the sample code), weird and wonderful things will happen, none of them good. By tweaking the values in the `sleep` duration calls, one can go from perfectly functioning code, to slightly baffling results (such as only 29 results being processed) to outright segfaults. ## A thread-safe Queue Given that the most common use case for a Queue is to decouple worker threads in the Producer/Consumer design pattern, it is worth considering how to improve the design of the `Queue<T>` class so that multi-threaded clients can use it. The code here is tagged at `thread-safe`: ``` git checkout thread-safe ``` Interestingly enough, we can leave the API almost unchanged, and we only need to add a `std::mutex` to guard access to the “wrapped” `std::queue`: ``` template<typename T> class ThreadsafeQueue { std::queue<T> queue_; mutable std::mutex mutex_; // Moved out of public interface to prevent races between this // and pop(). bool empty() const { return queue_.empty(); } public: ThreadsafeQueue() = default; ThreadsafeQueue(const ThreadsafeQueue<T> &) = delete ; ThreadsafeQueue& operator=(const ThreadsafeQueue<T> &) = delete ; ThreadsafeQueue(ThreadsafeQueue<T>&& other) { std::lock_guard<std::mutex> lock(mutex_); queue_ = std::move(other.queue_); } virtual ~ThreadsafeQueue() { } unsigned long size() const { std::lock_guard<std::mutex> lock(mutex_); return queue_.size(); } std::optional<T> pop() { std::lock_guard<std::mutex> lock(mutex_); if (queue_.empty()) { return {}; } T tmp = queue_.front(); queue_.pop(); return tmp; } void push(const T &item) { std::lock_guard<std::mutex> lock(mutex_); queue_.push(item); } }; ``` The most obvious change is the addition of a `std::mutex` to guard all accesses to the underlying `std::queue`, using the `std:lock_guard` pattern, to ensure that the lock is released by the holding thread under all possible exit scenarios (including exception being raised). A more subtle API change is the removal of the `empty()` method from public access: now clients can no longer query the Queue about its state, but can only infer its “emptiness state” by attempting to `pop` an element and obtaining an empty `optional`. The reason for this is that under no circumstances the Queue can guarantee that matters won’t change between the time the client queries `empty()` and the time `pop()` is called, making the point entirely moot, and this code a potential source of intermittent (read: hard to pin) bugs: ``` if (!queue.empty()) { // What could possibly go wrong? A lot, it turns out. auto elem = *queue.pop(); } ``` Obviously, there is a perfectly legitimate use case for the use of `empty()` as a simple “informational” (and, necessarily, ephemeral) assessment of the state of the Queue, but personally I just prefer to take the gun away from the child, as it would require (a) adding in large, bold font a warning to the user and (b) relying on the user to actually heed that warning. This is a more general approach to API design that holds, loosely: “Make interfaces easy to use right, and hard to use wrong” (originally attributed to Martin Fowler). ## About Locking and Performance There is a never-ending debate as to whether locks (and multi-threading in general) are pure evil and should be avoided at all costs; in particular, many folks feel that the gain to be had by using multi-core architectures is usually lost to the complexity and overhead to manage context switching, and locks acquisition (and release). The answer, as so many things in distributed systems, is “it depends”; and, most certainly it is foolish (if not outright fraudulent) to make statements about performance and optimizations without hard data and objective measurements; especially in view of the overall system’s stated goals and priorities. Be that as it may, if you believe that adopting a multi-threaded architecture would be beneficial to your system, and you have measured that using mutually exclusive locks (that’s actually what “mutex” stands for) does not negatively impact performance, this is a relatively efficient and simple-to-use implementation of a thread-safe queue.
Shard6 (laksa)
Root Hash7888718438869293406
Unparsed URLcom,codetrips!/2020/07/26/modern-c-writing-a-thread-safe-queue/ s443