2009-12-17 42 views
9

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

在likley倘上述文字是有点不清楚,这里的大致我想怎么能够做到?

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; 
    } 
} 

回答

9

好的,所以我只是从我的代码中剔除了这个,希望我删除了所有的应用程序逻辑。这个想法是,你使用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); 
+0

+1:使用带有重叠I/O的0字节读取作为接收数据时未实际读取任何数据的信号的方式非常有用,并且在文档中没有显而易见。 – 2010-09-18 23:16:43

1

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

如果可能的话,另一种选择是使用WCF,它几乎就是抽象。

+0

哦哇,一个答案!感谢:) 但我实际上解决了这个年龄前,但我现在把解决方案。 – 2010-03-21 00:14:46