命名管道 – 异步偷看

当在异步模式下打开的System.IO.Pipe.NamedPipeServerStream有更多可用于读取的数据时,我需要找到一种通知方式 – WaitHandle将是理想的。 我不能简单地使用BeginRead()来获取这样的句柄,因为我可能会被另一个想要写入管道的线程发出信号 – 所以我必须释放管道上的锁并等待写入完成,和NamedPipeServerStream没有CancelAsync方法。 我也尝试调用BeginRead(),然后如果线程被发出信号,则在管道上调用win32函数CancelIO,但我不认为这是一个理想的解决方案,因为如果在数据到达和处理时调用CancelIO,它将会被删除 – 我仍然希望保留这些数据,但是在写入之后稍后处理它。 我怀疑win32函数PeekNamedPipe可能有用,但我想避免不得不用它连续轮询新数据。

在上面的文字有点不清楚的情况下,这里大致是我想要做的…

NamedPipeServerStream pipe; ManualResetEvent WriteFlag; //initialise pipe lock (pipe) { //I wish this method existed WaitHandle NewDataHandle = pipe.GetDataAvailableWaithandle(); Waithandle[] BreakConditions = new Waithandle[2]; BreakConditions[0] = NewDataHandle; BreakConditions[1] = WriteFlag; int breakcode = WaitHandle.WaitAny(BreakConditions); switch (breakcode) { case 0: //do a read on the pipe break; case 1: //break so that we release the lock on the pipe break; } } 

好吧,所以我刚从我的代码中删除了这个,希望我删除了所有应用程序逻辑的东西。 这个想法是你尝试使用ReadFile进行零长度读取并等待lpOverlapped.EventHandle(读取完成时触发)和另一个线程想要写入管道时设置的WaitHandle。 如果由于写入线程而中断读取,请使用CancelIoEx取消零长度读取。

 NativeOverlapped lpOverlapped; ManualResetEvent DataReadyHandle = new ManualResetEvent(false); lpOverlapped.InternalHigh = IntPtr.Zero; lpOverlapped.InternalLow = IntPtr.Zero; lpOverlapped.OffsetHigh = 0; lpOverlapped.OffsetLow = 0; lpOverlapped.EventHandle = DataReadyHandle.SafeWaitHandle.DangerousGetHandle(); IntPtr x = Marshal.AllocHGlobal(1); //for some reason, ReadFile doesnt like passing NULL in as a buffer bool rval = ReadFile(SerialPipe.SafePipeHandle, x, 0, IntPtr.Zero, ref lpOverlapped); int BreakCause; if (!rval) //operation is completing asynchronously { if (GetLastError() != 997) //ERROR_IO_PENDING, which is in fact good throw new IOException(); //So, we have a list of conditions we are waiting for WaitHandle[] BreakConditions = new WaitHandle[3]; //We might get some input to read from the serial port... BreakConditions[0] = DataReadyHandle; //we might get told to yield the lock so that CPU can write... BreakConditions[1] = WriteRequiredSignal; //or we might get told that this thread has become expendable BreakConditions[2] = ThreadKillSignal; BreakCause = WaitHandle.WaitAny(BreakConditions, timeout); } else //operation completed synchronously; there is data available { BreakCause = 0; //jump into the reading code in the switch below } switch (BreakCause) { case 0: //serial port input byte[] Buffer = new byte[AttemptReadSize]; int BRead = SerialPipe.Read(Buffer, 0, AttemptReadSize); //do something with your bytes. break; case 1: //asked to yield //first kill that read operation CancelIoEx(SerialPipe.SafePipeHandle, ref lpOverlapped); //should hand over the pipe mutex and wait to be told to tkae it back System.Threading.Monitor.Exit(SerialPipeLock); WriteRequiredSignal.Reset(); WriteCompleteSignal.WaitOne(); WriteCompleteSignal.Reset(); System.Threading.Monitor.Enter(SerialPipeLock); break; case 2: //asked to die //we are the ones responsible for cleaning up the pipe CancelIoEx(SerialPipe.SafePipeHandle, ref lpOverlapped); //finally block will clean up the pipe and the mutex return; //quit the thread } Marshal.FreeHGlobal(x); 

通过MSDN,我没有看到任何机制来做你想要的。 最快的解决方案是使用互操作来访问PeekNamedPipe 。 如果您不想使用interop ,则可以在自定义类中抽象管道,并在抽象中提供查看function。 抽象将处理所有信令,并且必须协调读取和写入管道。 显然,这不是一项微不足道的任务。

如果可能,在您的情况下,另一种替代方案是研究使用WCF,这几乎就是抽象。