C++11 Thread-Safe Queue Implementation
Implementing a thread-safe queue primarily involves synchronizing basic operations such as enqueueing and dequeueing. In C++11, it can be implemented using standard library components including <mutex>, <condition_variable>, and <thread>.
Here is a simple design of a thread-safe queue:
cpp#include <queue> #include <mutex> #include <condition_variable> class ThreadSafeQueue { public: // Mutex: enables modification in const member functions mutable std::mutex mtx; // Standard library queue for storing data std::queue<T> data_queue; // Condition variable for synchronization std::condition_variable data_cond; // Disables copy constructor and copy assignment operator ThreadSafeQueue(const ThreadSafeQueue& other) = delete; // Acquires lock (non-blocking) void push(T new_value) { std::lock_guard<std::mutex> lock(mtx); data_queue.push(new_value); } // Non-blocking attempt to dequeue (does not wait for data) bool try_pop(T& value) { std::lock_guard<std::mutex> lock(mtx); if (data_queue.empty()) { return false; } value = std::move(data_queue.front()); data_queue.pop(); return true; } // Blocking dequeue operation (waits for data if necessary) void wait_and_pop(T& value) { std::unique_lock<std::mutex> lock(mtx); data_cond.wait(lock, [this]{ return !data_queue.empty(); }); value = std::move(data_queue.front()); data_queue.pop(); } // Checks if the queue is empty bool empty() const { std::lock_guard<std::mutex> lock(mtx); return data_queue.empty(); } // Returns the size of the queue size_t size() const { std::lock_guard<std::mutex> lock(mtx); return data_queue.size(); } };
Explanation:
This thread-safe queue implementation primarily relies on the following key components for synchronization and mutual exclusion:
- Mutex (
std::mutex): Ensures that only one thread can perform enqueue or dequeue operations at a time, preventing race conditions. - Condition Variable (
std::condition_variable): Blocks dequeue threads when the queue is empty and wakes waiting threads upon new element enqueue, enabling efficient producer-consumer coordination. - Locks (
std::lock_guardandstd::unique_lock): Automatically manage mutex locking and unlocking, ensuring proper release even during exceptions (e.g., via RAII).
Use Case Example:
This thread-safe queue is ideal for the producer-consumer model, where multiple producer threads continuously add tasks to the queue, and multiple consumer threads retrieve and execute these tasks. By utilizing a thread-safe queue, we can prevent data races and inconsistent states during task addition and extraction due to concurrent operations. For instance, in a multi-threaded server, producers might handle incoming requests while consumers process them, with the queue ensuring safe data transfer between threads.