【问题标题】:Most efficient way to send images across processes跨进程发送图像的最有效方式
【发布时间】:2011-02-01 22:34:56
【问题描述】:

目标

将一个进程生成的图像高效、高速地传递给另一个进程。这两个进程在同一台机器和同一桌面上运行。操作系统可能是WinXP、Vista和Win7。

详细说明

第一个过程仅用于控制与生成图像的设备的通信。这些图像的大小约为 500x300 像素,每秒可更新数百次。第二个过程需要这些图像来处理它们。第一个过程使用第三方 API 将图像从设备绘制到 HDC。这个 HDC 必须由我提供。

注意:两个进程之间已经打开了一个连接。它们通过匿名管道进行通信并共享内存映射文件视图。

想法

我将如何以尽可能少的工作来实现这一目标?我的意思是既为计算机工作,也为我工作(当然;))。我正在使用 Delphi,所以也许有一些组件可用于执行此操作?我想我总是可以绘制到任何图像组件的 HDC,将内容保存到内存流,通过内存映射文件复制内容,在另一侧解压缩并将其绘制到目标 HDC。我还阅读了有关可用于编组图像的 IPicture 接口。我需要它尽快,所以开销越少越好。我不希望仅仅通过复制一些图像来给机器带来压力。

你有什么想法?我很感激对此的每一个想法!

【问题讨论】:

  • 如果图像每秒更新数百次,您可能需要研究任何现有的视频处理技术来处理高带宽视频数据,而不是图像处理。

标签: windows delphi image ipc marshalling


【解决方案1】:

好吧,似乎内存映射文件和管道是正确的方法。这还不错,因为这两个进程已经共享一个 MMF 和两个管道(用于双向通信)。剩下要解决的唯一问题是如何以尽可能少的复制操作来传递数据。

效果不错的设计如下(顺序流程):

进程 1(想要图像)

  • 向进程 2(通过管道 1)发送信号以将图像存储在共享内存中
  • 进入睡眠状态并等待响应(阻止从管道 2 读取)

进程 2(提供图像)

  • on 信号(通过管道 1)唤醒并告诉硬件设备绘制到 HDC 1(这由共享内存支持,见下文)
  • 向进程 1 发出信号(通过管道 2)
  • 进入睡眠状态等待新工作(通过管道 1)

进程 1(想要图像)

  • 开启信号(通过管道 2)唤醒并从共享内存绘制到目标 HDC 2

现在通过共享内存传输图像(我的目标是使用不超过一个额外的复制操作):

进程 2 通过CreateDIBSection 创建一个HBITMAP 并提供文件映射的句柄和映射视图的偏移量。因此,图像数据存在于共享内存中。这将创建一个HBITMAP,该HBITMAP 被选择到 HDC 1(也由进程 2 创建)中,并且将从现在开始由进程 2 使用。

进程 1 使用 StretchDIBits 和指向映射视图内存的指针(如 here 所述)。这似乎是将位从内存直接获取到另一个 HDC(在本例中为 HDC 2)的唯一功能。其他函数会先将它们复制到某个中间缓冲区,然后您才能将它们从那里传输到最终的 HDC。

因此,最终需要传输的位似乎是开始时的两倍。但我认为这是最好的,除非可以在进程之间共享 GDI 句柄。

注意:我使用管道而不是信号,因为我还需要传输一些额外的数据。

【讨论】:

    【解决方案2】:

    如我所见,您有两种选择:

    1. 仅将图像句柄/指针传递给其他进程,因此这两个进程仅适用于一组图像。
    2. 将图像内容复制到其他进程并从那时起处理副本。

    哪种方法最好取决于您的设计。这两种方法的最佳工具是“内存映射文件”或“命名管道”。这是你能得到的最快的。内存映射文件可能是进程间通信的最快形式,但缺点是它们没有内置“客户端-服务器”范式。所以你必须自己同步对 MMF 的访问。另一方面,命名管道几乎一样快,但客户端-服务器范式直接构建在其中。速度上的差异主要来自于此。

    现在由于更新的共享速度,第一种方法可能会更好,但是您必须注意进程之间的同步,因此它们不会同时读取/写入单个图像。还可以使用某种缓存或其他智能技术,因此您可以将流量减少到最低限度。当面对如此高水平的沟通时,如果可能的话,总是建议寻找降低这种水平的方法。

    对于基于命名管道的 IPC 非常快速的实现,您可以使用我的 IPC 实现。它是面向消息的,因此您不必担心管道技术细节。它还在幕后使用线程池,并且具有最小的额外开销。 您可以对其进行压力测试并亲自查看(完整的客户端-服务器请求-响应周期,一条典型的消息需要 0.1 毫秒)。

    【讨论】:

    • 感谢您链接到您的 IPC 实施 - 如果我们需要跨越计算机边界,以后可能需要它。但是我现在的用例非常狭窄。我曾经读到不同的域可能会导致命名管道出现问题,您对此进行了测试吗?
    • 我在同一个局域网、同一个域的两台电脑上进行了测试。我没有尝试其他组合。我将它用于同一台计算机上的进程之间的通信,因为它比 MMF 更易于使用。无需进行任何同步。
    【解决方案3】:

    在某些情况下,您可以跨进程传递 HBITMAP 句柄。我以前见过它(是的,在 XP/Vista 上),当我的一位同事向我展示时,我和团队中的其他人一样感到惊讶。

    如果内存正确地为我服务,我相信如果 HBITMAP 分配有 GDI 函数之一(CreateBitmap、CreateCompatibleBitmap、CreateDIBitmap 等...)由 LoadBitmap 创建的 HBIMAP 句柄将不起作用,因为它只是一个指针到进程内资源。

    我认为,当您将 HBITMAP 共享给其他进程时,除了正常的 BitBlt 操作之外,不要尝试对其进行任何特殊操作。

    至少那是我记得的。我们很幸运,因为我们已经编写了图形库来将所有图像作为 HBITMAP 进行管理。

    YMMV

    【讨论】:

    • 这确实很有趣,虽然我也无法使用它而没有不好的感觉。跨进程传递 GDI 句柄似乎有点像赌博——它不应该起作用,但有时它似乎起作用。 (参见stackoverflow.com/questions/2499487/…,我首先询问了这种方法,因为我希望它会这么简单。)
    • 但是这个呢:stackoverflow.com/a/2500975/893350 我试过这个,我得到“GDI+ 中发生一般错误”。 ErrCode:-2147467259
    【解决方案4】:

    使用共享内存来传递图像数据,并使用其他东西(命名管道、套接字等)来协调切换。

    【讨论】:

      【解决方案5】:

      使用Memory Mapped File

      有关 Delphi 参考资料,请参阅 Memory-mapped Files in DelphiShared Memory in Delphi

      对于更通用的方法,您可以考虑使用管道或通过 TCP 发送位图数据。如果需要,这将允许您更轻松地在节点之间分配图像数据。

      【讨论】:

      • 是的,我可以使用内存映射文件来做到这一点。我仍然需要将图像绘制到我的 HDC,将图像序列化到 MMF,在另一侧再次读取它们并将图像绘制到最终的 HDC。似乎 4 个复制操作过去只有 1 个。因此我正在寻找一种更有效的方法。但如果没有,我认为这就是要走的路。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-06
      • 1970-01-01
      • 1970-01-01
      • 2015-05-06
      相关资源
      最近更新 更多