2

我尝试在专门针对设计模式/数据交换/类设计的问题中进行搜索,但无济于事。我专门用 C++ 编程,但这主要是一个设计问题,我认为这是一个很普遍的问题。

我正在尝试做的是设计至少两个类之间的数据交换,可能更多,如下所示:

  • 一类从磁盘读取图像并共享它们
  • 任意数量的类(0+)独立读取和处理这些图像

共享类不应受限于消费者类的存在。不是专家,我能想到的唯一选择是发布-订阅机制或使用共享内存。

这种问题有哪些可能的解决方案,以及它们的优缺点?

先感谢您

4

2 回答 2

1

您可以将其实现为经典的生产者-消费者模式。你没有提到生产者是否可以从不同的线程工作,但我会假设多线程能力使这个解决方案更加灵活。

// 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在我的代码中)可以是一个虚拟函数,它在专门的类中实现。

于 2018-01-31T10:18:54.917 回答
0

发布-订阅是一种非常通用的设计模式。实现它的一种方法是使用共享内存,但在网络上效果不佳。

我将假设您想要在进程内执行此操作,多线程。

由于数据似乎相当大,但是是静态的,您不应该将图像放在共享机制中,而是分配它并将指针传递给分配的内存。您可以在最后清理它,或者如果您不确定消费者何时完成它,请使用 std::shared_ptr。您也可以传递实际的图像数据,但这会导致它被复制多次。小的话应该没问题。

现在以线程安全的方式实现发布订阅者机制有点困难,而且容易搞砸。我建议使用库(似乎推荐使用 boost::signals2 )。

于 2018-01-31T16:05:24.403 回答