我尝试在专门针对设计模式/数据交换/类设计的问题中进行搜索,但无济于事。我专门用 C++ 编程,但这主要是一个设计问题,我认为这是一个很普遍的问题。
我正在尝试做的是设计至少两个类之间的数据交换,可能更多,如下所示:
- 一类从磁盘读取图像并共享它们
- 任意数量的类(0+)独立读取和处理这些图像
共享类不应受限于消费者类的存在。不是专家,我能想到的唯一选择是发布-订阅机制或使用共享内存。
这种问题有哪些可能的解决方案,以及它们的优缺点?
先感谢您
我尝试在专门针对设计模式/数据交换/类设计的问题中进行搜索,但无济于事。我专门用 C++ 编程,但这主要是一个设计问题,我认为这是一个很普遍的问题。
我正在尝试做的是设计至少两个类之间的数据交换,可能更多,如下所示:
共享类不应受限于消费者类的存在。不是专家,我能想到的唯一选择是发布-订阅机制或使用共享内存。
这种问题有哪些可能的解决方案,以及它们的优缺点?
先感谢您
您可以将其实现为经典的生产者-消费者模式。你没有提到生产者是否可以从不同的线程工作,但我会假设多线程能力使这个解决方案更加灵活。
// 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);
}
};
这是一个非常非常beta版的概念,但它应该给你一个概述。一些注意事项:
ImageProducer
类的成员,可以提供公共访问器来获取指向它的指针/引用 - 选择取决于特定需要。bool
标志(例如m_processingActive
,可能包含在 中std::atomic<>
)。此标志将在构造期间初始化为 true,并在生成最后一个图像后,由生产者更改为 false。当队列变为非活动状态时,消费者将结束等待图像。可能还有一些额外的改进,有些事情可能会有所不同,并且可能会更好。但是这个基本概念是一个很好的起点(我希望)。
当然,您不仅限于一个ImageConsumer
班级。实际处理函数(processImage
在我的代码中)可以是一个虚拟函数,它在专门的类中实现。
发布-订阅是一种非常通用的设计模式。实现它的一种方法是使用共享内存,但在网络上效果不佳。
我将假设您想要在进程内执行此操作,多线程。
由于数据似乎相当大,但是是静态的,您不应该将图像放在共享机制中,而是分配它并将指针传递给分配的内存。您可以在最后清理它,或者如果您不确定消费者何时完成它,请使用 std::shared_ptr。您也可以传递实际的图像数据,但这会导致它被复制多次。小的话应该没问题。
现在以线程安全的方式实现发布订阅者机制有点困难,而且容易搞砸。我建议使用库(似乎推荐使用 boost::signals2 )。