First of all, the documentation for EndRead does NOT explicitly say that an asynchronous read operation initiated by BeginRead is atomic or uninterruptable.
The Question
Is it possible to interrupt an asynchronous read operation started by FileStream.BeginRead, so that it finishes before filling the buffer, returning the number of bytes read into the buffer so far, or is it an all or nothing operation?
In other words, is there some method like "Cancel_IO", that I can call, such that when I call EndRead, instead of waiting for all possible bytes to be read, it returns earlier as as result of the read being cancelled?
Background
I've read the documentation of FileStream, BeginRead, and EndRead. EndRead does not have any overloads that are capable of triggering premature completion of the operation, returning a partially full buffer. I'm interested in whether anyone can confirm or deny the existence of a method in the Windows Operating System's API (Win32), or perhaps of a disk driver API, that could cause an operation initiated by FileStream.BeginRead to finish early when EndRead is called. By "early", I mean before filling the entire requested buffer length, without an error.
Use Case
For the sake of the unimaginative, assume the file is on a network share, and the network may sometimes experience extreme slow-downs, such that triggering the early completion of a generic 1MB buffering operation would be practical and optimal, in order to retrieve a few bytes for processing before resuming a new 1MB buffering operation.
Those "few bytes", could be used to initiate the construction of a number of computationally-intensive in-memory resources, which could be constructed while the buffering is allowed to finish.
About the Documentation
Note that the documentation of BeginRead does not explicitly state that the asynchronous operation is atomic or is uninterruptable. All it mentions is that if an "error" occurs, you won't know about it until EndRead is called. This does not preclude the possibility that some other event, which is not an error, could occur that would cause EndRead to return some number of bytes less than the number requested, which it does all the time anyway.
For example, "end of file" and "buffer full" can be though of as the two "natur开发者_开发百科al" interruptions of an asynchronous read operation, which cause it to return less than the number of bytes requested, without error. I'm looking for "artificial" interruption possibilities, which would also cause EndRead to successfully return the number of bytes read into the buffer, before the EOF and before the buffer is full.
Documentation explicitly says that: EndRead must be called with this IAsyncResult to find out how many bytes were read. On the other hand EndRead is blocking thread until read operation is completed. So, seems like read operation is atomic.
This is logical to me, since your scenario have a little of practical usage. If valuable information is stored in part of file being read, then you can always read it in smaller portions.
I read something in the documentation for windows synchronous and asynchronous I/O that may do the trick, but it would be a trick with uncertain consequences.
"If the handle is deallocated prematurely, ReadFile or WriteFile may incorrectly report that the I/O operation is complete."
Since the .NET BeginRead method is ultimately based on the Win32 ReadFile method, then acquiring and prematurely deallocating the handle may accomplish what I'm trying to do. The consequences of this will need researched.
It also mentions "To cancel all pending asynchronous I/O operations, use either: CancelIo or CancelIoEx", but those appears to cancel entire operations with failure (ERROR_OPERATION_ABORTED). I'm not sure whether any bytes read would have already been written to the buffer, and even if they were, one would not know how many were successfully written. I wonder if there's a way to trick the underlying system into thinking it has suddenly reached the end of the file or stream...
I also see that the "SetCommTimeouts" method has some interesting results, in particular surrounding the "COMMTIMEOUTS Structure's" "ReadIntervalTimeout" member, which claims that:
"If the interval between the arrival of any two bytes exceeds this amount, the ReadFile operation is completed and any buffered data is returned."
That seems promising...
In any case, the mere fact that I can cancel pending asynchronous I/O is useful. I could actually compute, using buffering stats and start times, whether it would be worth it to cancel the asynchronous read and re-issue a smaller read of the desired data chunk or whether it would be better to just wait for the operation to complete. It would depend on the calculated average speed of the stream, and how close it is to completion (based on it's computed/predicted progress value) and expected completion time, weighted against the relative utility of obtaining the desired data chunk.
No; there is no API that can do this.
Closing the handle is an old trick - it works way back to the NT days. However, it causes all outstanding operations on the handle to complete with an error.
Cancelling has similar problems - and note that CancelIoEx
may not be available on your platform.
SetCommTimeouts
is not an option, since it only works on serial ports and other communication device handles.
Cancellation has historically been one of the most difficult parts of writing a device driver. These days, the kernel has Cancel-Safe Queue support built-in to XP (back-portable to 2K), and it's a lot easier. But a lot of drivers (especially older drivers) just ignore cancellation anyway (which is legal).
I recommend implementing a "cancel" at a higher level of abstraction: close the handle or allow the operation to complete, and ignore the result.
精彩评论