implementing a timeout when reading a file with Delphi
I have an app written in Delphi 2006 that regularly reads from a disk file located elsewhere on a network (100Mb ethernet). Occasionally the read over the network takes a very long time (like 20 secs) and the app freezes, as the read is done from an idle handler in the main thread.
OK, I could put the read operation into it's own thread, but what I would like to know is whether it is possible to specify a timeout for a file operation, so that you can give up and go and do something else, or report the fact that the read has snagged a bit earlier than 20 seconds later.
function ReadWithTimeout (var Buffer ;
N : integer ;
Timeout : integer) : boolean ;
begin
Result := false
try
SetReadTimeout (Timeout) ; 开发者_运维问答 // <==========================???
FileStream.Read (Buffer, N) ;
Result := true ;
except
...
end ;
end ;
Open the file for asynchronous access by including the File_Flag_Overlapped
flag when you call CreateFile
. Pass in a TOverlapped
record when you call ReadFile
, and if the read doesn't complete immediately, the function will return early. You can control how long you wait for the read to complete by calling WaitForSingleObject
on the event you store in the TOverlapped
structure. You can even use MsgWaitForMultipleObjects
to wait; then you can be notified as soon as the read completes or a message arrives, whichever comes first, so your program doesn't need to hang at all. After you finish processing messages, you can check again whether the I/O is complete with GetOverlappedResult
, resume waiting, or give up on the I/O by calling CancelIo
. Make sure you read the documentation for all those functions carefully; asynchronous I/O isn't trivial.
After you've moved the read operation to a thread, you could store the value returned by timeGetTime before reading:
isReading := true;
try
startedAt := timeGetTime;
FileStream.Read (Buffer, N);
...
finally
isReading := false;
end;
and check in the idle handler if it's taken too long.
eg:
function ticksElapsed( FromTicks, ToTicks : cardinal ) : cardinal;
begin
if FromTicks < ToTicks
then Result := ToTicks - FromTicks
else Result := ( high(cardinal) - FromTicks ) + ToTicks; // There was a wraparound
end;
...
if isReading and ( ticksElapsed( startedAt, timeGetTime ) > 10 * 1000 ) // Taken too long? ~10s
then // Do something
精彩评论