Thread Safety for Stl Queue

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



Leave a reply



Submit