【问题标题】:Inter-process communication进程间通信
【发布时间】:2011-05-04 13:52:45
【问题描述】:

我有两个应用程序:X 和 Y。
X 是主应用程序,它处理大量 XML 文件。它已经有 10 多年的历史,已经使用了六种技术来存储、处理和处理这些 XML 文件。
Y 是我正在开发的一个调试工具,它可以处理和显示 XML 文件。人类可读的形式。基本上,它只有一组样式表,可以检测 XML 格式,如果它识别格式,它会将 XML 转换为 HTML,然后显示在 TWebBrowser 组件中。

问题:
当 Y 处于活动状态时,我希望 X 将其处理的任何 XML 发送到 Y 以进行显示。但只有在 Y 运行时!如果 Y 没有运行,X 就不会做任何事情。
Y 的检测需要随时进行,并且需要快速。我考虑过使用 TCP/IP 通信,但是缺少 Y 导致的延迟太长了。特别是因为有时会处理大量 XML。命名管道和类似的基于网络的解决方案也存在同样的问题。我需要快速确定 Y 是否正在运行且可用,如果是,则快速发送 XML,然后继续 X。
我还考虑过让 Y 成为基于 COM 的应用程序,或者添加基于 COM 的 DLL 和事件这将允许进程间通信。 DLL 解决方案会很有趣,因为它会向 X 公开一个上传 XML 文件的方法,然后向 Y 发送一个事件来处理 XML。 这似乎是最好的选择,尽管我还需要检查 DLL 是否已注册。如果没有,那么 X 甚至不能调用它!
应用程序 X 也将被没有收到 Y 或额外 DLL 的客户使用,因此在大多数情况下,DLL 不会被注册。 (正如我所说,它旨在帮助调试...)

但也许还有其他选择? TCP/IP 太慢,COM 有点太复杂。


X和Y会在同一个系统上运行。或者只有 X 在系统上,而 Y 完全丢失。


关于使用内存映射文件...虽然实用,但我需要记住,大多数时候,Y 不会运行,因此 MMF 会浪费内存。 XML 数据在 X 中的大小可以达到 4 MB,因此在内存中拥有多个这种大小的块有点过大了。它可用于在 X 和 Y 之间发送状态消息,但内存有时对应用程序 X 来说有点问题。虽然 MMF 可以连接到物理文件,但我试图完全避免写入任何临时文件。这是一个很好的解决方案,但我担心不够好。


我认为一些额外的解释是有序的。应用程序 X 是一个将使用几个小时的应用程序,用户会执行大量操作,这些操作会转换成大量的 XML 数据进行处理。应用程序 X 是一个桌面应用程序,它与多个 Web 应用程序 (REST)、Web 服务 (SOAP) 和其他应用程序进行通信,其中大部分是通过 XML。
应用程序 Y 只是为了窥探 X 正在运行的进程内部。基本上,X 工作 20 分钟,然后 Y 弹出。从那一刻起,X 应该开始向 Y 发送 XML,直到 Y 再次消失或 X 终止。在大多数情况下,Y 只会运行以捕获一小部分正在运行的任务,甚至可能多次启动。但我可能正在以错误的方向思考整个事情。也许 X 应该是一个 Y 注册到它的服务器...当 Y 找不到 X 时这不是一个真正的问题。但是 X 找不到 Y 不会导致延迟或其他问题...

【问题讨论】:

    标签: delphi winapi ipc delphi-2007


    【解决方案1】:

    根据不同的客户端/服务器调查,这里有一些关于速度的真实数据。

    所有基准测试均在一台计算机上本地运行。直接访问每秒可以实现超过 15000 次查询,HTTP/1.1 远程访问每秒可以实现 4300 次以上查询。这是在使用 Centrino2 CPU 的笔记本电脑上进行的基准测试,并启用了 AntiVirus。

    2.5. Client server access: 
      - Http client keep alive: 3001 assertions passed
         first in 7.87ms, done in 153.37ms i.e. 6520/s, average 153us
      - Http client multi connect: 3001 assertions passed
         first in 151us, done in 305.98ms i.e. 3268/s, average 305us
      - Named pipe access: 3003 assertions passed
         first in 78.67ms, done in 187.15ms i.e. 5343/s, average 187us
      - Local window messages: 3002 assertions passed
         first in 148us, done in 112.90ms i.e. 8857/s, average 112us
      - Direct in process access: 3001 assertions passed
         first in 44us, done in 41.69ms i.e. 23981/s, average 41us
      Total failed: 0 / 15014  - Client server access PASSED
    

    此基准测试同时测试客户端和服务器速度,并且不是多线程的(即使我们的框架是多线程安全的)。

    所以你可以猜到,对于每个请求一个 4 KB 的 JSON 内容数据块:

    1. 直接访问(如您的 dll 方法)是最快的,并且消耗的资源更少。
    2. 然后是 GDI 消息。
    3. 然后命名管道。
    4. 然后是 FastCGI 和 HTTP(取决于您的 FastCGI Web 服务器,HTTP 类的消耗量非常低)。

    保持活动连接是 HTTP/1.1 连接,多连接是纯 HTTP/1.0,每个请求都有一个新连接。多连接并没有那么糟糕,因为从服务器的角度来看,我们使用了一个基于 I/O 完成端口的高效线程池。

    http://synopse.info/forum/viewtopic.php?id=90

    【讨论】:

    • 我们的HTTP层很薄,所以你可以从上面的HTTP结果中猜出TCP连接时间。
    • 对我来说最大的问题不是发送数据的速度,而是 X 检测到 Y 所需的时间。但这些都是实际的统计数据,似乎 DLL 选项是最有趣的一个……
    • GDI 消息?也许您打算说user 消息?
    【解决方案2】:

    在连接上运行一个 TCP 服务器(作为触发器)非常快,应该做你想做的事

    【讨论】:

    • 除非服务器没有运行,在这种情况下客户端会花一些时间搜索服务器。我需要避免这种延迟,尤其是在大多数情况下,用户不会让服务器运行。此外,服务器可能随时启动和停止,因此在应用程序运行时服务器一直在上升、下降、上升、下降。 (应用程序将执行较长的进程,查看器只是为了快速浏览一下进程。)
    【解决方案3】:

    查看我的 IPC:

    http://www.cromis.net/blog/downloads/cromis-ipc/

    它快速、免费并且具有可设置的超时时间,因此您可以将其设置为非常小的量(例如 50 毫秒)。因为它非常快(典型的消息周期请求 -> 处理 -> 响应需要不到 1 毫秒,大约 0.1 毫秒),所以您可以有非常小的超时。它内置了客户端服务器,所以很多客户端都没有问题。它在任务池后面运行线程,因此它不会冻结您的程序,并且它具有非常灵活的数据包,可以轻松写入/读取数据。

    如前所述,您甚至可以通过其他方式检查调试器是否正在运行。

    • 检查进程
    • 检查进程的主窗口
    • 使用互斥体
    • ...

    【讨论】:

    【解决方案4】:

    如果你愿意使用临时文件作为传输,那就很容易了

    1. 两个程序在启动时注册相同的自定义消息 MsgAppNotifyID := RegisterWindowMessage(......);

    2. 程序 Y 必须具有处理自定义消息的代码 使用 SetWindowLong 或任何等效项

    3. 程序 X,使用 FindWindow 查看程序 Y 是否正在运行

    4. 程序 X,如果 Y 正在运行,则使用一致的文件名,例如 XXYY1234 (XXYYn) 格式,在两端的已知位置/目录上创建一个临时文件,其中 1234=n

    5. 程序 X,使用 BroadcastSystemMessage 向程序 Y 发出信号 NotifyRecipients := BSM_APPLICATIONS; 广播系统消息(BSF_IGNORECURRENTTASK 或 BSF_POSTMESSAGE 或 BSF_FORCEIFHUNG、@NotifyRecipients、MsgAppNotifyID、任何自定义值、n);

    6. 程序 y,使用 WParam 作为 n 处理上述消息并为进程重建文件

    干杯 祝你好运

    【讨论】:

    • 其实没有。我想避免使用临时文件。
    【解决方案5】:

    我会去内存映射文件的方向,但我不会直接在代码路径中实现..相反我会通过一个中间对象来执行对内存映射文件的写入,这样它可以被替换为如果数据不到位,则将其丢弃。

    当程序第一次启动(或被告知通过配置更改检查)时,系统将创建存根“什么都不做”或“通过内存映射文件记录日志”对象。这也使您能够在需要时添加稍后的调试器...例如用于网络日志记录的 UDP 记录器等。

    您可以使用“FindWindow”调用来查看您的调试器是否正在运行。

    【讨论】:

      【解决方案6】:

      您可以更简单地执行此操作,因为您只是想了解一个应用程序是否从另一个应用程序运行。只要它们由同一用户在同一台​​机器上运行,您就可以让 X 简单地使用 FindWindow() 来查看 Y 当前是否正在运行。只需确保给 Y 一个有意义的名称(在下面的示例中,它是 TXMLFormatterForm):

      var
        XMLWindow: HWnd;
      begin
        XMLWindow := FindWindow('TXMLFormatterForm', nil);
        if XMLWindow > 0 then
          // Y is running
      end;
      

      你也可以使用 Y 的窗口标题(标题),只要你确定它是不同的:

      XMLWindow := FindWindow(nil, 'Workshop Alex's XML Formatter');
      

      【讨论】:

      • @Workshop Alex:一旦知道 Y 正在运行,就可以使用任何其他进程间通信方法。 TCP over localhost 对我来说非常非常快。如果您希望 X 专注于它的处理,请考虑在单独的线程(可能来自线程池)中执行写入操作。
      • 问题是,每个程序都可以随时再次停止。因此,该检查需要一遍又一遍地执行。不过,它很快。然后需要解决 IPC 本身,以一种快速建立连接的方式,每次调用...
      • 好吧,做一个 FindWindow 来检查查看器是否存在。然后将第二条消息发送到该窗口,该窗口返回一个特殊的 ID。然后使用此 ID 创建一个命名管道,它们将共享该命名管道以发送数据。然后应用程序将再向查看器发送一条消息以再次关闭命名管道。效果很好... :-)
      【解决方案7】:

      命名管道速度很快,因为它们基于内存映射文件来实现。服务器故障时的超时可能会很慢......

      如果您需要在同一台计算机上快速响应,为什么不使用好的旧 GDI 消息?

      即使没有用户界面或可视形式,您也可以在普通控制台或后台服务应用程序中使用这些消息(如果是服务应用程序,安全设置必须指定此服务必须与桌面交互,即必须能够接收和发送消息)。

      诀窍是处理 WM_COPYDATA 消息。 例如,参见我们的 TSQLRestClientURIMessage 类,以及 TSQLRestServer 的 ExportServerMessage/AnswerToMessage 方法,在 http://synopse.info/fossil/finfo?name=SQLite3/SQLite3Commons.pas 中实现

      在实践中,我发现对于少量数据(每条消息最多 64 KB 等),GDI 消息比命名管道快得多。

      Looking for an alternative to windows messages used in inter-process communication你有其他选择

      【讨论】:

      • 好的 - 我没有阅读你的关于你想要交流的数据的信息。对于 4MB 的数据,WM_COPYDATA 不是最佳选择。
      • 我有一个基于命名管道的 IPC 解决方案,它不会受到大超时的影响。它也非常快。
      • @Runner 超时发生在服务器故障的情况下(请参阅我在第一段中的观点)。我写它是一种实现 IPC 的快速方法(Windows 实现是基于内存映射文件和信号,正如我在第一句话中所说的)。
      • 我知道为什么会发生超时我只是说在我的解决方案中您可以控制超时发生的时间。很抱歉说的很明显。我看到你写了关于 MMF 和速度的文章 :)
      【解决方案8】:

      您可以让 X 将其输出写入 memory-mapped file - 如果 Y 正在运行,则它可以检索数据。这样 X 不关心 Y 是否向上。

      X 可以在已知位置写入某种控制信息(例如,在映射文件中存储从偏移量 0 开始的最后 1000 个写入 XML 的偏移量)并将文件的其余部分用作原始数据的循环缓冲区.

      如果您需要 Y 作为 X 中操作的决定因素,请让 Y 创建映射文件,然后使用它的存在/不存在来检查“通道”的 X 端的数据生成。有创建者和第二个用户here的示例代码。

      【讨论】:

        【解决方案9】:

        使用内存映射文件。它们非常适合您的特定任务。如果您没有时间或打算实施自定义方案,我们的 MsgConnect 及其 MMF 传输将提供帮助。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-03-28
          • 2010-11-19
          • 2013-11-13
          相关资源
          最近更新 更多