【问题标题】:Named Pipes - Asynchronous Peeking命名管道 - 异步窥视
【发布时间】:2009-12-17 04:02:38
【问题描述】:

当以异步模式打开的 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;
     }
}

【问题讨论】:

    标签: c# .net asynchronous named-pipes overlapped-io


    【解决方案1】:

    好的,所以我刚刚从我的代码中删除了这个,希望我删除了所有应用程序逻辑的东西。这个想法是您尝试使用 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);
    

    【讨论】:

    • +1:使用 0 字节读取和重叠 I/O 作为在写入数据而不实际读取任何数据时接收信号的方式非常有用,并且在文档中没有明确说明。
    【解决方案2】:

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

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

    【讨论】:

    • 哇哦,答案!谢谢 :) 我实际上在很久以前就解决了这个问题,我现在就提出解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-06
    相关资源
    最近更新 更多