首先,EndRead的文档 NOT 明确表示由BeginRead启动的异步读取操作是原子的或不可中断的。
是否可以中断由FileStream.BeginRead启动的异步读取操作,以便在填充缓冲区之前完成,返回到目前为止读入缓冲区的字节数,还是全部或全部操作?
换句话说,是否有一些类似“Cancel_IO”的方法,我可以调用,这样当我调用EndRead时,它不会等待读取所有可能的字节,而是在读取被取消的结果之前返回?
我已经阅读了FileStream,BeginRead和EndRead的文档。 EndRead没有任何能够触发过早完成操作的重载,返回部分满的缓冲区。我感兴趣的是,是否有人可以确认或否认Windows操作系统的API(Win32)中存在的方法,或者可能是磁盘驱动程序API,这可能会导致FileStream.BeginRead在EndRead发生时提前完成操作调用。 “早”,我的意思是在填充整个请求的缓冲区长度之前,没有错误。
为了缺乏想象力,假设文件在网络共享上,并且网络有时可能经历极端减速,因此触发通用1MB缓冲操作的早期完成将是实用且最佳的,以便按顺序在恢复新的1MB缓冲操作之前检索几个字节进行处理。
这些“少量字节”可以用于启动构建大量计算密集型内存资源,这些资源可以在允许缓冲完成时构建。
请注意,BeginRead的文档未明确声明异步操作是原子操作或不可中断。所有提到的是,如果发生“错误”,在调用EndRead之前你不会知道它。这并不排除可能会发生一些其他非错误的事件,这会导致EndRead返回的请求数量少于所请求的数量,无论如何都会这样做。
例如,“文件结束”和“缓冲区已满”可以作为异步读取操作的两个“自然”中断,这会导致它返回少于请求的字节数而没有错误。我正在寻找“人为”中断的可能性,这也会导致EndRead在EOF之前和缓冲区满之前成功返回读入缓冲区的字节数。
答案 0 :(得分:2)
Documentation明确说明:必须使用此IAsyncResult调用 EndRead以查找读取的字节数。另一方面,EndRead阻塞线程,直到读取操作完成。所以,看起来读操作是原子的。
这对我来说是合乎逻辑的,因为你的场景有一些实际用途。如果有价值的信息存储在正在读取的文件的一部分中,那么您可以始终以较小的部分读取它。
答案 1 :(得分:0)
我在documentation for windows synchronous and asynchronous I/O中读到了可以解决问题的一些内容,但这将是一个具有不确定后果的技巧。
“如果句柄过早释放,则可以使用ReadFile或WriteFile 错误地报告I / O操作已完成。“
由于.NET BeginRead方法最终基于Win32 ReadFile方法,因此获取并过早释放句柄可能会完成我正在尝试的操作。这样做的后果需要研究。
它还提到“要取消所有挂起的异步I / O操作,请使用:CancelIo或CancelIoEx”,但这些操作似乎会取消整个操作失败(ERROR_OPERATION_ABORTED)。我不确定是否已经将任何读取的字节写入缓冲区,即使它们是,也不知道有多少成功写入。我想知道是否有办法欺骗底层系统认为它突然到达文件或流的末尾......
我还看到“SetCommTimeouts”方法有一些有趣的结果,特别是围绕“COMMTIMEOUTS Structure”的“ReadIntervalTimeout”成员,声称:
“如果任意两个字节到达之间的间隔超过此值 amount,ReadFile操作完成,任何缓冲的数据都是 返回。“
这似乎很有希望......
在任何情况下,我可以取消挂起的异步I / O这一事实很有用。我实际上可以使用缓冲统计和开始时间计算是否值得取消异步读取并重新发出所需数据块的较小读取,或者等待操作完成是否更好。这将取决于计算的流的平均速度,以及它与完成的距离(基于其计算/预测的进度值)和预期的完成时间,与获得所需数据块的相对效用进行加权。
答案 2 :(得分:0)
没有;没有API可以做到这一点。
关闭手柄是一个老技巧 - 它可以回到NT时代。但是,它会导致句柄上的所有未完成操作都出现错误。
取消有类似问题 - 请注意您的平台上可能无法使用CancelIoEx
。
SetCommTimeouts
不是一个选项,因为它只适用于串行端口和其他通信设备句柄。
取消历史一直是编写设备驱动程序最困难的部分之一。目前,内核具有内置于XP的取消安全队列支持(可移植到2K),并且它更容易。但是很多司机(特别是老司机)无论如何都会忽略取消(这是合法的)。
我建议在更高的抽象级别实现“取消”:关闭句柄或允许操作完成,并忽略结果。