I implemented the following class to dispatch std::function<void(void)>
objects to a thread pool. Multiple threads will block on the pop call until a task is available.
I am using the C++14 standard.
#pragma once
#include<memory>
#include<queue>
#include<mutex>
#include<condition_variable>
namespace alpha {
template<typename T>
class AsyncQueue {
private:
std::shared_ptr<std::queue<T>> queue;
std::shared_ptr<std::mutex> mutex;
std::shared_ptr<std::condition_variable> cond;
public:
/* Big 5 */
AsyncQueue(const AsyncQueue& other) = default;
AsyncQueue(AsyncQueue&& other) = default;
AsyncQueue& operator=(const AsyncQueue& other) = default;
AsyncQueue& operator=(AsyncQueue&& other) = default;
virtual ~AsyncQueue() = default;
/**
* @brief Default constructor
*/
AsyncQueue() :
queue(new std::queue<T>()),
mutex(new std::mutex),
cond(new std::condition_variable()) {
}
/**
* @brief Push a value to the async queue
*/
void push(const T& object) {
{
std::lock_guard<std::mutex> lock(*mutex);
queue->push(object);
}
cond->notify_one();
}
/**
* @brief Push a value to the async queue (move variant)
*/
void push(T&& object) {
{
std::lock_guard<std::mutex> lock(*mutex);
queue->push(object);
}
cond->notify_one();
}
/**
* @brief Pop a value from the queue
*/
T&& pop() {
std::unique_lock<std::mutex> lock(*mutex);
while(queue->empty()) {
cond->wait(lock);
}
T object = std::move(queue->front());
queue->pop();
return std::move(object);
}
};
}
Specific questions:
- Is there a way to keep both implementations of
push
DRY? The only difference is the type of the argument. - Is this safe? I tested it multiple times with
true; while [[ $? == 0 ]]; do ./test; done
, but i'd feel a lot better if someone told me that my idea of thread-safety is correct. - pop returns a rvalue-reference, which seems to cause problems when passing it directly to a function with the argument type
const T&
. Assigning it to a local variable first solves that, but is there a way to make push return a rvalue-reference only when it is needed? I am asking because i know C++ compilers implicitly move a lot.
EDIT: The version that has the suggestions included can be found here. I applied the BSD 3-clause license to it.
virtual
destructor is inheritance. Do other classes inherit publicly fromAsyncQueue
? If not, then don't give it a virtual destrutor and possibly also mark the class asfinal
. \$\endgroup\$