2009-12-17 41 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