Implement a thread-safe queue
A thread-safe queue is a data structure designed to be used concurrently by multiple threads without data corruption or race conditions. Ensuring thread safety is crucial in multithreaded applications where multiple threads may attempt to access or modify the queue simultaneously. A thread-safe queue typically employs synchronization mechanisms to provide safe and ordered access to its elements.
#include <iostream>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <thread>
template <typename T>
class ThreadSafeQueue {
private:
std::queue<T> queue_;
mutable std::mutex mutex_;
std::condition_variable condition_;
public:
void enqueue(const T& item) {
std::lock_guard<std::mutex> lock(mutex_);
queue_.push(item);
condition_.notify_one(); // Notify a waiting consumer
}
T dequeue() {
std::unique_lock<std::mutex> lock(mutex_);
condition_.wait(lock, [this] { return !queue_.empty(); });
T item = queue_.front();
queue_.pop();
return item;
}
};
void producer(ThreadSafeQueue<int>& q, int id) {
for (int i = 0; i < 5; ++i) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
q.enqueue(id * 100 + i);
}
}
void consumer(ThreadSafeQueue<int>& q, int id) {
for (int i = 0; i < 5; ++i) {
int item = q.dequeue();
std::cout << "Consumer " << id << " dequeued: " << item << std::endl;
}
}
int main() {
ThreadSafeQueue<int> tsQueue;
std::thread producer1(producer, std::ref(tsQueue), 1);
std::thread producer2(producer, std::ref(tsQueue), 2);
std::thread consumer1(consumer, std::ref(tsQueue), 1);
std::thread consumer2(consumer, std::ref(tsQueue), 2);
producer1.join();
producer2.join();
consumer1.join();
consumer2.join();
return 0;
}
Output:
Consumer 1 dequeued: Consumer 1002 dequeued: 200
Consumer Consumer 2 dequeued: 1011 dequeued:
201
Consumer 2 dequeued: 102
Consumer 2 dequeued: 202
Consumer 2 dequeued: 103
Consumer 1 dequeued: 203
Consumer 1 dequeued: 104
Consumer 1 dequeued: 204
In this example:
- The
ThreadSafeQueue
class has methodsenqueue
to add an item to the queue anddequeue
to retrieve an item from the queue. std::mutex
is used to guard access to the shared queue, ensuring that only one thread can modify the queue at a time.std::condition_variable
is used to coordinate between the producer and consumer threads. The consumer threads wait for the queue to become non-empty before attempting to dequeue.- The
producer
function adds items to the queue, and theconsumer
function retrieves items from the queue.