【发布时间】:2012-03-26 19:40:48
【问题描述】:
WaitNamedPipe 函数允许管道客户端应用程序同步等待命名管道服务器上的可用连接。然后调用CreateFile 作为客户端打开管道。伪代码:
// loop works around race condition with WaitNamedPipe and CreateFile
HANDLE hPipe;
while (true) {
if (WaitNamedPipe says connection is ready) {
hPipe = CreateFile(...);
if (hPipe ok or last error is NOT pipe busy) {
break; // hPipe is valid or last error is set
}
} else {
break; // WaitNamedPipe failed
}
}
问题在于这些都是阻塞的、同步的调用。什么是异步执行此操作的好方法?例如,我似乎找不到使用重叠 I/O 来执行此操作的 API。例如,对于管道 servers,ConnectNamedPipe 函数提供了一个 lpOverlapped 参数,允许服务器异步等待客户端。然后管道服务器可以调用 WaitForMultipleObjects 并等待 I/O 操作完成,或任何其他事件发出信号(例如,一个事件信号通知线程取消挂起的 I/O 并终止)。
我能想到的唯一方法是在一个短暂的、有限超时的循环中调用WaitNamedPipe,并在超时时检查其他信号。或者,在循环调用CreateFile 中,检查其他信号,然后在短时间内调用Sleep(或WaitNamedPipe)。例如:
HANDLE hPipe;
while (true) {
hPipe = CreateFile(...);
if (hPipe not valid and pipe is busy) {
// sleep 100 milliseconds; alternatively, call WaitNamedPipe with timeout
Sleep(100);
// TODO: check other signals here to see if we should abort I/O
} else
break;
}
但在我看来,这种方法臭不可闻。如果管道有一段时间不可用,线程会继续运行 - 占用 CPU、使用电源、需要内存页面保留在 RAM 中等。在我看来,依赖于 Sleep 或短超时的线程不会表现良好,是多线程编程草率的标志。
但是在这种情况下有什么替代方案?
【问题讨论】:
-
一个明显的解决方案是将调用 WaitNamedPipe 放在一个单独的线程中。 IIRC,一些异步 IO 函数实际上在后台使用线程,所以这并不像听起来那么低效。
-
告诉我们更多关于这段代码正在解决的竞争条件。 MS 文档示例客户端代码说首先调用 CreateFile,并且只有在 Create 失败且 Pipe Busy 时才调用 WaitNamedPipe。循环执行,等待适当的超时。根据我的经验,这一直有效。该错误仅在两个客户使用一个管道时发生,但这不是一场比赛。一个客户端连接,另一个等待并重试,这与您的 Sleep 示例发生的情况相同,但更简洁。
-
@Mark:在文档中。当 WaitNamedPipe 成功时,CreateFile 可能仍然失败,因为另一个线程先跳入。这是一个竞争条件:两个或更多线程正在竞相打开管道。将循环放入围绕竞争条件工作,因此在 OPs 代码中的注释。不过,这不是他要我们解决的问题。
-
扩展答案。基本上,Poster 的
CreateFile/Sleeploop 是解决这个问题的完美解决方案 - 无需进一步。 -
@Ben:不,这不是问题,但很痛苦。它使你的代码更难维护,因为模块化被破坏了。
标签: c++ c windows named-pipes