You could implement it as a classic producer-consumer pattern. You did not mention whether producers can work from different threads, but I will assume multi-threading capability to make this solution more flexible.
// Not important what this actually is.
class Image
{ };
using ImagePtr = std::shared_ptr<Image>;
// Shared queue which stores currently available images and
// encapsulates synchronization details.
class ImageQueue
{
private:
std::queue<ImagePtr> m_images;
std::mutex m_mutex;
std::condition_variable m_cond;
public:
void PostImage(std::shared_ptr<Image> image)
{
// Lock the queue, push image, notify single thread.
std::unique_lock<std::mutex> lock(m_mutex);
m_images.push(image);
m_cond.notify_one();
}
ImagePtr WaitForImage()
{
// Lock the queue, wait if empty, fetch image and return it.
std::unique_lock<std::mutex> lock(m_mutex);
if (m_images.empty())
{
m_cond.wait(lock, [&m_images]() -> bool { return !m_images.empty(); });
}
assert (!m_images.empty());
auto nextImage = m_images.front();
m_images.pop();
return nextImage;
}
};
// Image producer class, loads images and posts them into the queue.
class ImageProducer
{
private:
ImageQueue* m_queue;
public:
void LoadImage(const char* file)
{
auto image = loadAndInitializeImageObject(file);
m_queue->PostImage(image);
}
};
// Image consumer class, fetches images and processes them.
class ImageConsumer
{
private:
ImageQueue* m_queue;
public:
void ProcessImage()
{
auto image = m_queue->WaitForImage();
processImage(image);
}
};
This a very, very beta-version concept, but it should give you an overview. Some notes:
- There should be, of course, a single queue instance. It could be instantiated independently and passed to both classes as a constructor argument (via pointer or reference), but it also could be a member of
ImageProducer
class which could provide a public accessor to obtain the pointer/reference to it - the choice depends on particular needs.
- Currently, the logic does not include clear point when processing should end. Queue could have an additional
bool
flag (e.g. m_processingActive
, possibly wrapped in std::atomic<>
). This flag would be initialized to true during construction and, after last image is produced, changed to false by the producer. Consumers would end waiting for images when queue becomes inactive.
There are probably some additional improvements, some things may be done differently and, possibly, better. But this basic concept is a quite good starting point (I hope).
Of course, you are not limited to a single ImageConsumer
class. Actual processing function (processImage
in my code) could be a virtual function, which is implemented in specialized classes.