使用Delphi读取文件时实现超时功能

5
我有一个用Delphi 2006编写的应用程序,它经常从网络上其他位置的磁盘文件中读取数据(100Mb以太网)。偶尔,从网络上读取数据需要很长时间(例如20秒),导致应用程序冻结,因为在主线程的空闲处理程序中进行读取操作。
好的,我可以将读取操作放入其自己的线程中,但我想知道是否可以指定文件操作的超时时间,以便您可以放弃并去做其他事情,或者报告读取操作卡住的事实比20秒早一点。
function ReadWithTimeout (var Buffer     ;
                              N       : integer ; 
                              Timeout : integer) : boolean ;

begin
Result := false
try
    SetReadTimeout (Timeout) ;          //  <==========================???
    FileStream.Read (Buffer, N) ;
    Result := true ;
except 
    ... 
    end ;
end ;
2个回答

9

调用 CreateFile 时,包括 File_Flag_Overlapped 标志以打开文件并允许异步访问。调用ReadFile 时传入一个 TOverlapped 记录,如果读取不能立即完成,则该函数将早期返回。您可以通过在 TOverlapped 结构中存储的事件上调用 WaitForSingleObject 来控制等待读取完成的时间长度。甚至可以使用 MsgWaitForMultipleObjects 等待;然后,只要读取完成 有消息到达,您就会收到通知,因此程序不需要挂起。处理完消息后,您可以使用 GetOverlappedResult 再次检查 I/O 是否已完成,恢复等待,或通过调用 CancelIo 放弃 I/O。请确保仔细阅读所有这些函数的文档; 异步 I/O 并不简单。


我相信这是可以做到的,但我会把这个任务留给比我更有能力(或者更有动力)的人,@Workshop。正如我所说,这并不是一件简单的事情,所以我不会即兴编写一个例子。 - Rob Kennedy

0

在将读取操作移动到线程之后,您可以在读取之前存储由timeGetTime返回的值:

isReading := true;
try
  startedAt := timeGetTime;
  FileStream.Read (Buffer, N);
  ...
finally
  isReading := false;
end;

并在空闲处理程序中检查是否花费了太长时间。

例如:

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

问题是:如果线程中的读取操作出现故障,它将保持文件处于打开状态,因此即使我检测到它已经超过1秒,我也无法重试,直到线程达到finally子句,即直到20秒已经过去。 - rossmcm
根据您打开文件时使用的共享模式,您应该能够打开第二个句柄,并尝试在不同的线程中并行执行。但是,您应该注意问题,例如覆盖共享数据或启动过多的线程。 - TheNewbie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接