Implementierung eines Timeout, wenn eine Datei mit Delphi lesen
-
25-09-2019 - |
Frage
Ich habe eine app in Delphi 2006 geschrieben, die regelmäßig von einer Plattendatei an anderer Stelle in einem Netzwerk liest (100Mb Ethernet). Gelegentlich wird die Lese über das Netzwerk eine sehr lange Zeit in Anspruch nimmt (wie 20 Sekunden) und die App gefriert, als das Lesen aus einem Ruhe-Handler in dem Haupt-Thread ausgeführt wird.
OK, ich könnte die Lese Betrieb genommen in einen eigenen Thread ist, aber was ich möchte wissen, ob es möglich ist, ein Timeout für eine Dateioperation angeben, dass so können Sie aufgeben und gehen und etwas anderes tun, oder die Tatsache, berichtet, dass die Lese etwas früher als 20 Sekunden später verhakt hat.
function ReadWithTimeout (var Buffer ;
N : integer ;
Timeout : integer) : boolean ;
begin
Result := false
try
SetReadTimeout (Timeout) ; // <==========================???
FileStream.Read (Buffer, N) ;
Result := true ;
except
...
end ;
end ;
Lösung
Öffnen Sie die Datei für asynchrone Zugriff durch die File_Flag_Overlapped
Flagge einschließlich des CreateFile
nennen. Der Pass in einer TOverlapped
Aufzeichnung, wenn Sie ReadFile
rufen, und wenn die Lese nicht sofort abgeschlossen ist, wird die Funktion zurückgeben früh. Sie können steuern, wie lange Sie für das Lesen, um eine vollständige warten von WaitForSingleObject
auf den Fall ruft Sie in der TOverlapped
Struktur speichern. Sie können sogar MsgWaitForMultipleObjects
zu warten, verwenden; dann können Sie, sobald die Lese abgeschlossen ist oder eine Nachricht eintrifft, benachrichtigt je nachdem was zuerst kommt, so dass Ihr Programm überhaupt nicht hängen muss. Nachdem Sie Nachrichten beenden Verarbeitung, können Sie noch einmal überprüfen, ob die E / A mit GetOverlappedResult
, Lebenslauf Warte abgeschlossen ist, oder telefonisch unter CancelIo
auf dem I / O aufgeben. Stellen Sie sicher, dass Sie die Dokumentation für alle diese Funktionen sorgfältig lesen; asynchrones I / O ist nicht trivial.
Andere Tipps
Nachdem Sie den Lesevorgang zu einem Thread verschoben haben, können Sie den Wert von timeGetTime zurück speichern, bevor das Lesen:
isReading := true;
try
startedAt := timeGetTime;
FileStream.Read (Buffer, N);
...
finally
isReading := false;
end;
und die Prüfung im Ruhe Handler, wenn es zu lange gedauert hat.
Beispiel:
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