C# 命名管道性能问题

C# 命名管道性能问题,c#,delphi,io,named-pipes,C#,Delphi,Io,Named Pipes,我使用命名管道在C#和Delphi之间进行过程间通信。C#使用System.IO.Pipes包,而Delphi使用Libby的Pipes.pas。不幸的是,通信几乎是高性能的:分析表明,通信占用了整个运行时的72%,其余部分用于计算。 我找到了一个可能占用资源的问题:如果我没有在Delphi中显式断开发送客户端的连接,C#根本不会接收任何数据 德尔福(发送) C#(接收) 德尔福(接收) 也许您可以使用命名事件来发送IPC信号。 当这些是本地的(TEvent.Create('local\myse

我使用命名管道在C#和Delphi之间进行过程间通信。C#使用
System.IO.Pipes
包,而Delphi使用
Libby的Pipes.pas
。不幸的是,通信几乎是高性能的:分析表明,通信占用了整个运行时的72%,其余部分用于计算。
我找到了一个可能占用资源的问题:如果我没有在Delphi中显式断开发送客户端的连接,C#根本不会接收任何数据

德尔福(发送) C#(接收) 德尔福(接收)
也许您可以使用命名事件来发送IPC信号。 当这些是本地的(TEvent.Create('local\myserver')时,它们在Win7等中可以正常工作; 当您需要在不同会话(例如客户端应用程序和后台windows服务)之间执行IPC时,您需要更多权限等(默认全局\由于UAC原因无法在win7中使用?)。

例如:为每个连接创建一个事件(为每个连接生成一个名称)

或者查看另一个名为pipe+事件的IPC实现:

顺便说一句:你提到你使用了评测,但你不能说什么花费的时间最多? 你使用了什么样的分析工具?不是像这样的“分析工具”
阿奎斯特(http://smartbear.com/products/free-tools/aqtime-standard)或自动分析器(http://code.google.com/p/asmprofiler/)?

简单的改进可能是:首先发送要发送的字节数(以便接收方知道它可以期望多少数据),然后在大量(手动)之后发送数据通过分析,我对这个问题提出了两个见解:

  • Libby的管道是一个复杂的野兽。因为它似乎使用了多个线程,并且在使用方面表现出奇怪的行为,所以手动使用WinApi毕竟更方便。此外,实际通信的性能提高了。换句话说,在这样一个相对简单的IPC场景中,Libby的管道似乎是比WinApi慢
  • Annonymous管道/使用stdout和stdin似乎比命名管道更快
  • 然而,我必须补充一点,我仍然有点困惑,不知道这是真的还是我在这里算错了数字

    下面是一个简单的示例,演示了在Delphi中WinApi实现的样子:

    // setup pipes, you'll need one for each direction
    // init handles with 0
        CreatePipe(ReadPipe1,       // hReadpipe
                   WritePipe1,      // hWritePIpe
                   @SecurityAttributes,        // Security
                   PIPE_SIZE)                  // Size
    
        // setup Startupinfo
        FillChar(StartupInfo, Sizeof(StartupInfo), 0);
        StartupInfo.cb := Sizeof(StartupInfo);
        StartupInfo.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
        StartupInfo.hStdInput := ReadPipe1;
        StartupInfo.hStdOutput := WritePipe2;
        StartupInfo.wShowWindow :=  SW_HIDE; 
    
        // CreateProcess [...]
    
        // read
        Win32Check(
                ReadFile(
                      ReadPipe1,  // source
                      (@outputBuffer[1])^,               // buffer-pointer
                      PIPE_BUFFER_SIZE,                 // size
                      bytesRead,                       // returns bytes actually read
                      nil                             // overlapped on default
                      ));
        // send           
        Win32Check(
                WriteFile(
                    WritePipe2,
                    (@msg[1])^,         // lpBuffer - workarround to avoid type cast
                    NumberOfBytesToWrite,
                    bytesWritten,       // lpNumberOfBytesWritten
                    nil                 // Overlapped   
                    ));                          
    

    也许不需要断开连接,而是尝试发送一个换行符,这样C端就可以处理它的
    Readline()
    调用。在字符串中添加以#13#10形式的换行符没有帮助。从外观上看,C部分在断开连接之前不会识别任何数据。究竟是什么吃掉了72%?C#/Delphi发送/接收?两边的整个管道通信,即C#->Delphi,反之亦然。注意:提到的“其他”实现使用特殊的开始+结束标记来表示传输的开始或结束(MB_end等)。然而,此实现仅限于delphi…感谢您的快速响应。我已经在使用这个“改进的”pipes.pas,因为libby的原始网站甚至不再在线(archive.org的最后一个条目是从2010年开始).您有没有关于如何使用这些令牌的示例?源代码没有太大的帮助-我不知道如何使用TPipeThread(它实现了MB_*-常量)。顺便说一句,我使用了AQtime和一个自定义的时间测量,在代码中的重要点以毫秒和CPU滴答为单位对时间进行了协议化。AQtime无法逐行分析代码。我对Delphi和C#之间的命名管道做得不多,也许你可以发布Delphi和C#的一个小型演示应用程序()这将如何提高性能?如果我在实际消息之前发送消息的长度,我需要另一个discenct来通知C来读取它。我非常确定系统API已经使用了缓冲结构。不,通常不需要断开连接来发送信号!只需先发送字节数,在C中读取是数量,读取一个字节数组,直到有相同的数量,这样C#就知道它什么时候准备好了。
    // Wait for a client to connect
    stc.pipeServer.WaitForConnection();
    while (reconnect_attempts < MAX_RECONNECT_ATTEMPTS) // 
    {
       string tmp = sr.ReadLine();
    
       // if result is empty, try again for <MAX_RECONNECT_ATTEMPTS> times
       // so you can eliminate the chance that there's just a single empty request
       while (tmp != null)// && result != tmp)
       {
          tmp = sr.ReadLine();
          result += tmp;
       }
       // sleep, increment reconnect, write debugging...
    }
    stc.pipeServer.Close();
    
     stc.pipeClient.Connect();
     StreamWriter sw = new StreamWriter(stc.pipeClient);
     //sw.AutoFlush = true;
     sw.WriteLine(msg);
     sw.Flush();
     stc.pipeClient.WaitForPipeDrain();  // waits for the other end to read all bytes 
     // neither disconnect nor dispose
    
     SetLength(S, Stream.Size);   Stream.Read(S[1], Length(S));  
     FPipeBuffer := FPipeBuffer + S;   { TODO 2 : switch case ID }   
    // if the XML is complete, i.e. ends with the closing checksum   
    if (IsFullMessage()) then
    begin
       // end reading, set flag
       FIsPipeReady := true;
    end
    
    // setup pipes, you'll need one for each direction
    // init handles with 0
        CreatePipe(ReadPipe1,       // hReadpipe
                   WritePipe1,      // hWritePIpe
                   @SecurityAttributes,        // Security
                   PIPE_SIZE)                  // Size
    
        // setup Startupinfo
        FillChar(StartupInfo, Sizeof(StartupInfo), 0);
        StartupInfo.cb := Sizeof(StartupInfo);
        StartupInfo.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
        StartupInfo.hStdInput := ReadPipe1;
        StartupInfo.hStdOutput := WritePipe2;
        StartupInfo.wShowWindow :=  SW_HIDE; 
    
        // CreateProcess [...]
    
        // read
        Win32Check(
                ReadFile(
                      ReadPipe1,  // source
                      (@outputBuffer[1])^,               // buffer-pointer
                      PIPE_BUFFER_SIZE,                 // size
                      bytesRead,                       // returns bytes actually read
                      nil                             // overlapped on default
                      ));
        // send           
        Win32Check(
                WriteFile(
                    WritePipe2,
                    (@msg[1])^,         // lpBuffer - workarround to avoid type cast
                    NumberOfBytesToWrite,
                    bytesWritten,       // lpNumberOfBytesWritten
                    nil                 // Overlapped   
                    ));