Why is std::queue not thread-safe?
Imagine you check for !queue.empty()
, enter the next block and before getting to access queue.first()
, another thread would remove (pop) the one and only element, so you query an empty queue.
Using a synchronized queue like the following
#pragma once
#include <queue>
#include <mutex>
#include <condition_variable>
template <typename T>
class SharedQueue
{
public:
SharedQueue();
~SharedQueue();
T& front();
void pop_front();
void push_back(const T& item);
void push_back(T&& item);
int size();
bool empty();
private:
std::deque<T> queue_;
std::mutex mutex_;
std::condition_variable cond_;
};
template <typename T>
SharedQueue<T>::SharedQueue(){}
template <typename T>
SharedQueue<T>::~SharedQueue(){}
template <typename T>
T& SharedQueue<T>::front()
{
std::unique_lock<std::mutex> mlock(mutex_);
while (queue_.empty())
{
cond_.wait(mlock);
}
return queue_.front();
}
template <typename T>
void SharedQueue<T>::pop_front()
{
std::unique_lock<std::mutex> mlock(mutex_);
while (queue_.empty())
{
cond_.wait(mlock);
}
queue_.pop_front();
}
template <typename T>
void SharedQueue<T>::push_back(const T& item)
{
std::unique_lock<std::mutex> mlock(mutex_);
queue_.push_back(item);
mlock.unlock(); // unlock before notificiation to minimize mutex con
cond_.notify_one(); // notify one waiting thread
}
template <typename T>
void SharedQueue<T>::push_back(T&& item)
{
std::unique_lock<std::mutex> mlock(mutex_);
queue_.push_back(std::move(item));
mlock.unlock(); // unlock before notificiation to minimize mutex con
cond_.notify_one(); // notify one waiting thread
}
template <typename T>
int SharedQueue<T>::size()
{
std::unique_lock<std::mutex> mlock(mutex_);
int size = queue_.size();
mlock.unlock();
return size;
}
The call to front()
waits until it has an element and locks the underlying queue so only one thread may access it at a time.
std::queue pop push thread safety
No, it's not. The standard containers are not thread-safe -- you can't mutate them from two threads. You will have to use a mutex, or a lock-free queue. The problem is that the std::queue
has to be able to work with objects like std::string
, which cannot be atomically moved or constructed, and the std::queue
also has to support arbitrary sizes.
Most lock-free queues only work with machine-word size types and a fixed maximum size. If you need the flexibility of std::queue
and thread-safety, you'll have to manually use a mutex. Adding the mutex to the default implementation would be also extremely wasteful, as now suddenly every application would get thread safety even if it doesn't need it.
is std::queue thread safe with producer and multiple consumers
You must protect access to std::queue
. If you are using boost protect it using boost::mutex
. Now if you have multiple readers and one writer thread look at boost::shared_lock
(for readers) and boost::unique_lock
(for writer).
However if you will encounter writer thread starvation look at boost::shared_mutex
.
ThreadSafe Queue c++
It doesn't mean the program isn't thread safe. It doesn't mean it's ill-defined and can crash.
It just means your program's logic is not written to add the items to the queue in any particular order.
If you want those two items to be added in a specific order, push both from one thread.
Thread safety doesn't mean your application runs as if it only had one thread.
Your program is working fine.
Is the following std::queue code example thread safe?
So my question is: Is this example thread safe?
No.
Or do I need a mutex for the front() function?
Yes.
And is the mutex necessary for the functions pop() and push()?
Yes.
Modifying a standard container is not guaranteed to be thread safe.
C++11 thread-safe queue
According to the standard condition_variables
are allowed to wakeup spuriously, even if the event hasn't occured. In case of a spurious wakeup it will return cv_status::no_timeout
(since it woke up instead of timing out), even though it hasn't been notified. The correct solution for this is of course to check if the wakeup was actually legit before proceding.
The details are specified in the standard §30.5.1 [thread.condition.condvar]:
—The function will unblock when signaled by a call to notify_one(), a call to notify_all(), expiration of the absolute timeout (30.2.4) specified by abs_time, or spuriously.
...
Returns: cv_status::timeout if the absolute timeout (30.2.4) specifiedby abs_time expired, other-ise cv_status::no_timeout.
Pointer to STL Container Thread Safety (Queue/Deque)
You are worried that modifying a container (adding/deleting nodes) in one thread will somehow invalidate the pointer to the container in another thread. The container is an abstraction and will remain valid unless you delete the container object itself. The memory for the data maintained by the containers are typically allocated on the heap by stl::allocators
.
This is quite different from the memory allocated for the container object itself which can be on the stack, heap etc., based on how the container object itself was created. This separation of the container
from the allocator
is what's preventing some modification to the data from modifying the container object itself.
To make debugging your problem simpler, like Jonathan Reinhart suggests, make it a single threaded system, that reads the stream AND parses it.
On a side note, have you considered using Boost Lookfree Queues or something similar. They are designed exactly for this type of scenarios. If you were receiving packets/reading them frequently, locking the queue for reading/writing for each packet can become a significant performance overhead.
Related Topics
What Are Practical Applications of Weak Linking
Preventing Gcc from Automatically Using Avx and Fma Instructions When Compiled with -Mavx and -Mfma
Explicit Call to Destructor Is Not Destroying My Object Why
Why Does C++11 Not Support Anonymous Structs, While C11 Does
Fast Multiplication/Division by 2 for Floats and Doubles (C/C++)
Double Dispatch/Multimethods in C++
Random Number Generator - Why Seed Every Time
Std::Vector Calling Destructor Multiple Times During Push_Back
#Error Please Use the /Md Switch for _Afxdll Builds
What Happens to the Memory Allocated by 'New' If the Constructor Throws
Multiple "Could Not Be Resolved" Problems Using Eclipse with Mingw
Boost::Spirit How to Parse and Call C++ Function-Like Expressions