我需要找到一种方法,当在异步模式下打开的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;
}
}
答案 0 :(得分: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);
答案 1 :(得分:1)
通过MSDN,我没有看到任何机制来做你想要的。最快的解决方案是使用互操作来访问PeekNamedPipe
。如果您不想使用interop
,则可以在自定义类中抽象管道,并在抽象中提供查看功能。抽象将处理所有信令,并且必须协调读取和写入管道。显然,这不是一项微不足道的任务。
另一种选择,如果可能的话,在你的情况下,是考虑使用WCF,这几乎就是抽象。