我正在使用命名管道进行C#和Delphi之间的程序间通信. C#使用System.IO.Pipes包,而Delphi使用Libby的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 / 11kb)总共需要130(对于11kb为270ms)(发送和接收).
我的问题是:
是否有必要强制断开管道以表明客户端已完成写入?就我的观察而言,只有在使用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
最佳答案 经过大量(手动)分析后,我想出了两个关于问题的见解:
>利比的管子是一头复杂的野兽.由于它似乎使用多个线程并且显示出与其使用有关的奇怪行为,因此手动使用WinApi更加方便.此外,实际通信所采取的性能也有所提高.换句话说:在这样一个相对简单的IPC场景中,libby的管道似乎比WinApi慢.
>匿名管道/使用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
));