同步和重叠管道 I/O

ReadFileWriteFileTransactNamedPipeConnectNamedPipe 函数可以在管道上同步或异步执行输入和输出操作。 当函数同步运行时,它不会返回,直到它正在执行的操作完成。 这意味着,调用线程的执行可能会无限期地被阻止,同时等待耗时的操作完成。 当函数异步运行时,即使操作尚未完成,它也会立即返回。 这样,当调用线程可以自由执行其他任务时,就可以在后台执行耗时的操作。

使用异步 I/O 使管道服务器能够使用执行以下步骤的循环:

  1. 在对 wait 函数的调用中指定多个事件对象,并等待其中一个对象设置为信号状态。
  2. 使用 wait 函数的返回值确定已完成的重叠操作。
  3. 执行清理已完成的操作所需的任务,并为该管道句柄启动下一个操作。 这可能需要为同一管道句柄启动另一个重叠操作。

重叠操作使一个管道可以同时读取和写入数据,使单个线程可以在多个管道句柄上同时执行 I/O 操作。 这使单线程管道服务器能够有效地处理与多个管道客户端的通信。 有关示例,请参阅 使用重叠 I/O 的命名管道服务器

要使管道服务器使用同步操作与多个客户端通信,它必须为每个管道客户端创建单独的线程,以便一个或多个线程可以在其他线程等待时运行。 有关使用同步操作的多线程管道服务器的示例,请参阅 多线程管道服务器

启用异步操作

仅当为指定的管道句柄启用重叠模式并指定指向 OVERLAPPED 结构的有效指针时,才能异步执行 ReadFileWriteFileTransactNamedPipeConnectNamedPipe 函数。 如果 OVERLAPPED 指针为 NULL,则函数返回值可能会错误地指示操作已完成。 因此,强烈建议使用 FILE_FLAG_OVERLAPPED 创建句柄,并且需要异步行为,应始终指定有效的 OVERLAPPED 结构。

指定的 OVERLAPPED 结构的 hEvent 成员必须包含手动重置事件对象的句柄。 这是 CreateEvent 函数创建的同步对象。 启动重叠操作的线程使用事件对象来确定操作何时完成。 在同一句柄上执行同步操作时,不应使用管道句柄进行同步,因为无法知道哪个操作的完成导致了管道句柄的信号。 在同一管道句柄上同时执行操作的唯一可靠方法是对每个操作使用单独的 OVERLAPPED 结构及其自己的事件对象。 有关事件对象的详细信息,请参阅 同步

此外,可以使用 GetQueuedCompletionStatus 或 GetQueuedCompletionStatusEx 函数在重叠操作完成时收到通知。 在这种情况下,无需在 OVERLAPPED 结构中分配手动重置事件,并且完成操作针对管道句柄的方式与异步读取或写入操作相同。 有关详细信息,请参阅 I/O 完成端口

异步执行 ReadFileWriteFileTransactNamedPipeConnectNamedPipe 操作时,会发生以下情况之一:

  • 如果在函数返回时操作已完成,则返回值指示操作的成功或失败。 如果发生错误,则返回值为零, GetLastError 函数返回ERROR_IO_PENDING以外的内容。
  • 如果函数返回时操作尚未完成,则返回值为零, GetLastError 返回ERROR_IO_PENDING。 在这种情况下,调用线程必须等待操作完成。 然后,调用线程必须调用 GetOverlappedResult 函数来确定结果。

使用完成例程

ReadFileExWriteFileEx 函数提供另一种形式的重叠 I/O。 与重叠的 ReadFileWriteFile 函数(它们使用事件对象发出完成信号)不同,扩展函数指定 完成例程。 完成例程是在读取或写入操作完成后排队等待执行的函数。 在调用 ReadFileExWriteFileEx 的线程通过调用 fAlertable 参数设置为 TRUE 的可警报等待函数之一来启动可警报等待操作之前,不会执行完成例程。 在可发出警报的等待操作中,当 ReadFileExWriteFileEx 完成例程排队等待执行时,函数也会返回 。 管道服务器可以使用扩展函数对连接到它的每个客户端执行一系列读取和写入操作。 序列中的每个读取或写入操作指定一个完成例程,每个完成例程启动序列中的下一步。 有关示例,请参阅 命名管道服务器使用完成例程