命名管道性能问题

时间:2012-10-16 11:42:05

标签: c# delphi io named-pipes

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

德尔福(发送)

FClient1.Write(msg[1], Length(msg));
FClient1.FlushPipeBuffers;
FClient1.WaitForReply(20);
FClient1.Disconnect;   // disconnect to signalize C# that the writing is finished
FClient1.Connect;      // connect again to prevent synchronization problems

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();

即使我认为重新连接很昂贵,但我并不完全确定。一个数据流(大约1/11 kb)总共需要130个(11kb为270毫秒)(发送和接收)。

我的问题是
是否有必要强制断开管道以表明客户端已完成写入?就我的观察而言,仅在使用libby 发送时才需要。表现不佳还有其他可能的原因吗?提前谢谢。

另外,这里的发送和接收完成相反:

C#(发送)

 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

3 个答案:

答案 0 :(得分:2)

经过大量(手动)分析后,我想出了两个关于这个问题的见解:

  1. 利比的管子是一头复杂的野兽。由于它似乎使用多个线程并且显示出与其使用有关的奇怪行为,因此手动使用WinApi更加方便。此外,实际通信所采取的性能也有所提高。换句话说:在这样一个相对简单的IPC场景中,libby的管道似乎比WinApi慢。
  2. 匿名管道/使用stdout&amp; stdin似乎比命名管道更快。
  3. 但是,我必须补充一点,我仍然有点困惑,不知道这是否属实,或者我在这里一直在处理错误的数字。

    以下是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   
                    ));                          
    

答案 1 :(得分:0)

也许您可以将命名事件用于IPC信令。 当这些是本地的时,这些在Win7等中工作正常(TEvent.Create('local \ myserver'); 当您需要在不同会话之间进行IPC(例如客户端应用程序和后台Windows服务)时,您需要更多权限等(由于UAC,默认全局\不能在win7中使用?)。 http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/devwin32/threadswaitingforatasktobecompleted_xml.html

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

或者看一下不同的IPC命名管道+事件实现: https://micksmix.wordpress.com/2011/06/27/named-pipes-unit-for-delphi/

顺便说一下:你提到过你使用过性能分析但是你不能说最需要花费多少时间? 你使用了什么样的分析?不是像“探险家”那样 AQtime(http://smartbear.com/products/free-tools/aqtime-standard)或AsmProfiler(http://code.google.com/p/asmprofiler/)?

答案 2 :(得分:0)

简单的改进可能是:首先发送要发送的字节数(让接收者知道它可以预期多少数据)然后发送数据