Reputation: 168
What is a better designpattern for creating a blocking bufferqueue, that uses resources efficiently in C++11/14 without too many allocs/moves? Using Queue<std::unique_ptr<Workitem>>
OR
Queue<Workitem>
and with that hiding the resource management in the implementation (a bit like stl containers). Note, the second idea (Queue) is commented out. What are the implications of the commented out version also regarding heap/stack? And what about using std::unique_ptr<Queue<Workitem>> q
?
I am not very good with c++, but regardless of the version, I can't really leak memory right? (reasonging: not new/delete -> no memoryleaks)
Code here:
#include <condition_variable>
#include <mutex>
#include <queue>
template <class T> class Queue {
public:
Queue(size_t size);
// push item to _queue if _queue.size() < _size
// else block
void push(T item);
// void push(std::unique_ptr<T> item);
// pop item if !_queue.empty()
// else block or return false after timeout
bool pop(T &item);
// bool pop(std::unique_ptr<T> &item);
private:
std::mutex _mutex;
std::size_t _size;
std::queue<T> _queue;
// std::queue<std::unique_ptr<T>> _queue;
std::condition_variable _condition_full;
std::condition_variable _condition_empty;
};
struct Workitem {
size_t idx;
void *workdetails;
};
void do_work(Queue<std::unique_ptr<Workitem>> &work_q,
Queue<std::unique_ptr<Workitem>> &write_q,
struct options_s &opts) {
std::unique_ptr<Workitem> work;
while (work_q.pop(work)) {
// calculation w/ work
std::unique_ptr<Workitem> res = consume(work, opts);
write_q.push(std::move(work));
}
}
void do_write(Queue<std::unique_ptr<Workitem>> &write_q,
struct options_s &opts) {
std::unique_ptr<Workitem> work;
while (write_q.pop(work)) {
prepare_for_writing(work, opts); // clean item
write(work);
}
}
auto w1 = std::thread(do_work, std::ref(work_q), std::ref(write_q),
std::ref(options));
auto w2 = std::thread(do_work, std::ref(work_q), std::ref(write_q),
std::ref(options));
auto writer = std::thread(do_write, std::ref(write_q), std::ref(options));
int main() {
Queue<std::unique_ptr<Workitem>> work_q{4};
Queue<std::unique_ptr<Workitem>> write_q{4};
// Queue<Workitem> q{4};
// ??? std::unique_ptr<Queue<Workitem>> q{4} ???
for (size_t i, ...) { // do many iterations
std::unique_ptr<Workitem> w{};
// Workitem w{};
populate(w, i); // populate work item
work_q.push(std::move(w));
}
w1.join();
w2.join();
writer.join();
}
I can give the implementation if that helps, I just didn't want to clutter everything so I left it out. As a remark, the queue is used by threads. I use two queues for multiple worker threads and one writer thread to spread the load across cores.
cheers
Upvotes: 0
Views: 776
Reputation: 238311
What is a better designpattern for creating a blocking bufferqueue, that uses resources efficiently in C++11/14 without too many allocs/moves? Using
Queue<std::unique_ptr<Workitem>>
ORQueue<Workitem>
Given that criteria, Queue<Workitem>
is better because it avoids one layer of dynamic allocation.
What are the implications of the commented out version also regarding heap/stack?
There aren't really any differences regarding heap versus stack. The objects are dynamically allocated in both variations.
I can't really leak memory right? (reasonging: not new/delete -> no memoryleaks)
Your reasoning is sound. The example code that you show has no memory leaks.
There however is a bare pointer member variable Workitem::workdetails
that may have potential for memory leaks if it is misused (if it ever owns memory that it points to).
Upvotes: 2