![]() |
FileCopy im Thread
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo,
ich brauche in einer Anwendung eine Funktion, die große Dateien 1MB - 3GB aus einer Quelle (A) in zwei Ziele (B) und (C) kopiert, wobei B und C im Netzwerk liegen. Die erste Version kopierte einfach lesen von A schreiben nach B lesen von A schreiben nach C Die zweite Version lief schon optimierter und sparte ca 30% Zeit ein lesen von A schreiben nach B schreiben nach C Die dritte MyFirstThreadStepsVersion spart zusätzlich nochmal rund 15% der Zeit ein, Sie arbeitet mit 2 Puffern, mit denen parallel gelesen und geschrieben wird. Die Version funktioniert, nur wenn ich mir den Code ansehe, :oops: sieht man wohl, daß es sich um erste Gehversuche mit Threads handelt. Ich habe das mal als kleine Testanwendung als Lazarus Projekt (im Anhang) gebaut. Vielleicht hat ja einer der Thread-Gurus hier Lust und Zeit mal reinzuschauen und ein paar Tips zu geben. LG und PS: Fehlerprüfungen sind noch keine eingebaut, es ging mir erstmal ums Prinzip (Delphi = BDS2006) |
AW: FileCopy im Thread
Wärst du auch bereit eine komplett neue Unit mit einer neuen Kopierroutine zu verwenden?
Ich habe hier gerade eine rumliegen, damals irgendwo gefunden und immer wieder mal angepasst. Damit kopiere ich eine 3,01 GB große Datei von meiner zweiten Festplatte D auf meine Festplatte Y die über USB-10-MBit am Router angeschlossen ist in 4 Minuten inklusive detaillierter GUI-Anzeige, Berechnungen im Hintergrund etc. Dabei stelle ich mir hier noch eine Frage. würde es den Kopiervorgang nicht auch verschnellern, wenn man als Buffer immer die Dateigröße nimmt statt einen festen Buffer? Bis 50 MB natürlich nur! D.h. wenn ich eine Datei mit 10 KB kopiere ist der Buffer 10 KB groß. Ist es eine Datei von 3 GB, dann maximal 50 MB. |
AW: FileCopy im Thread
Sicher könnte/würde ich auch eine andere besser programmierte Lösung nutzen.
Im Test habe ich mit einer der festen Größe 16MB gearbeitet, weil ich es nur mit einer 3GB Testdatei teste und 16MB sich als die performanteste Größe herausgestellt hat. Wenn man weniger z.B. 8MB oder mehr z.B. 32MB nimmt, werden die Kopierzeiten länger. In der Version ohne Thread wird die Größe dynamisch angepasst.
Delphi-Quellcode:
buffersize := 1024 * 1024 * 16; // 16MB
if ( Size < buffersize ) and ( Size > 0 ) then buffersize := size; SetLength( buffer, BufferSize ); |
AW: FileCopy im Thread
Zitat:
|
AW: FileCopy im Thread
Theoretisches:
![]() ![]() Man kann auch ![]() xcopy /J - Kopiert ohne E/A-Puffer. Für große Dateien empfohlen. |
AW: FileCopy im Thread
Hier mal meine Unit die ich schon ewig verwende und ein wenig angepasst habe und irgendwo mal gefunden habe.
Delphi-Quellcode:
Aufruf zum Beispiel mit Callback
unit uFastFileCopy;
interface uses Windows, SysUtils; type TFastCopyFileMode = (fcfmCreate, fcfmAppend); TFastCopyFileNormalCallback = procedure(const FileName: TFileName; const CurrentSize, TotalSize: Int64; var CanContinue: Boolean); TFastCopyFileMethodCallback = procedure(const FileName: TFileName; const CurrentSize, TotalSize: Int64; var CanContinue: Boolean) of object; const BufferSize_Default: Cardinal = 4096 * 3 * 2; // Simplest definition function FastCopyFile(const ASourceFileName, ADestinationFileName: TFileName): Boolean; overload; // Definition with CopyMode and without any callbacks and default buffer function FastCopyFile(const ASourceFileName, ADestinationFileName: TFileName; CopyMode: TFastCopyFileMode): Boolean; overload; // Definition with normal procedure callback and default buffer function FastCopyFile(const ASourceFileName, ADestinationFileName: TFileName; CopyMode: TFastCopyFileMode; Callback: TFastCopyFileNormalCallback): Boolean; overload; // Definition with normal procedure callback and custom buffer function FastCopyFile(const ASourceFileName, ADestinationFileName: TFileName; CopyMode: TFastCopyFileMode; Callback: TFastCopyFileNormalCallback; BufferSize: Cardinal) : Boolean; overload; // Definition with object method callback and custom buffer function FastCopyFile(const ASourceFileName, ADestinationFileName: TFileName; CopyMode: TFastCopyFileMode; Callback: TFastCopyFileMethodCallback; BufferSize: Cardinal) : Boolean; overload; implementation {Dummy Callback: Method Version} type TDummyCallBackClient = class(TObject) private procedure DummyCallback(const FileName: TFileName; const CurrentSize, TotalSize: Int64; var CanContinue: Boolean); end; procedure TDummyCallBackClient.DummyCallback(const FileName: TFileName; const CurrentSize, TotalSize: Int64; var CanContinue: Boolean); begin // Nothing CanContinue := True; end; {Dummy Callback: Classical Procedure Version} procedure DummyCallback(const FileName: TFileName; const CurrentSize, TotalSize: Int64; var CanContinue: Boolean); begin // Nothing CanContinue := True; end; {CreateFileW API abstract layer} function OpenLongFileName(ALongFileName: string; DesiredAccess, ShareMode, CreationDisposition: LongWord): THandle; var // IsUNC: Boolean; FileName: PWideChar; begin // Translate relative paths to absolute ones ALongFileName := ExpandFileName(ALongFileName); // Check if already an UNC path // IsUNC := Copy(ALongFileName, 1, 2) = '\\'; // if not IsUNC then // ALongFileName := '\\?\' + ALongFileName; // Preparing the FileName for the CreateFileW API call FileName := PWideChar(WideString(ALongFileName)); // Calling the API Result := CreateFileW(FileName, DesiredAccess, ShareMode, nil, CreationDisposition, FILE_ATTRIBUTE_NORMAL, 0); end; {FastCopyFile implementation} function FastCopyFile(const ASourceFileName, ADestinationFileName: TFileName; CopyMode: TFastCopyFileMode; Callback: TFastCopyFileNormalCallback; Callback2: TFastCopyFileMethodCallback; BufferSize: Cardinal): Boolean; overload; var Buffer: TArray<Byte>; ASourceFile, ADestinationFile: THandle; FileSize, TotalBytesWritten: Int64; BytesRead, BytesWritten, BytesWritten2, CreationDisposition: LongWord; CanContinue, CanContinueFlag: Boolean; begin FileSize := 0; TotalBytesWritten := 0; CanContinue := True; SetLength(Buffer, BufferSize); // Manage the Creation Disposition flag CreationDisposition := CREATE_ALWAYS; if CopyMode = fcfmAppend then CreationDisposition := OPEN_ALWAYS; // Opening the source file in read mode ASourceFile := OpenLongFileName(ASourceFileName, GENERIC_READ, 0, OPEN_EXISTING); if ASourceFile <> 0 then try FileSize := FileSeek(ASourceFile, Int64(0), FILE_END); FileSeek(ASourceFile, Int64(0), FILE_BEGIN); SysUtils.ForceDirectories(ExtractFilePath(ADestinationFileName)); // Opening the destination file in write mode (in create/append state) ADestinationFile := OpenLongFileName(ADestinationFileName, GENERIC_WRITE, FILE_SHARE_READ, CreationDisposition); if ADestinationFile <> 0 then try // If append mode, jump to the file end if CopyMode = fcfmAppend then FileSeek(ADestinationFile, Int64(0), FILE_END); // For each blocks in the source file while CanContinue and (FileSeek(ASourceFile, Int64(0), FILE_CURRENT) < FileSize) do begin // Reading from source if (ReadFile(ASourceFile, Buffer[0], BufferSize, BytesRead, nil)) and (BytesRead <> 0) then begin // Writing to destination WriteFile(ADestinationFile, Buffer[0], BytesRead, BytesWritten, nil); // Read/Write secure code block (e.g. for WiFi connections) if BytesWritten < BytesRead then begin WriteFile(ADestinationFile, Buffer[BytesWritten], BytesRead - BytesWritten, BytesWritten2, nil); Inc(BytesWritten, BytesWritten2); if BytesWritten < BytesRead then RaiseLastOSError; end; // Notifying the caller for the current state Inc(TotalBytesWritten, BytesWritten); CanContinueFlag := True; if Assigned(Callback) then Callback(ASourceFileName, TotalBytesWritten, FileSize, CanContinueFlag); CanContinue := CanContinue and CanContinueFlag; if Assigned(Callback2) then Callback2(ASourceFileName, TotalBytesWritten, FileSize, CanContinueFlag); CanContinue := CanContinue and CanContinueFlag; end; end; finally CloseHandle(ADestinationFile); end; finally CloseHandle(ASourceFile); end; // Check if cancelled or not if not CanContinue then if FileExists(ADestinationFileName) then DeleteFile(ADestinationFileName); // Results (checking CanContinue flag isn't needed) Result := (FileSize <> 0) and (FileSize = TotalBytesWritten); end; {FastCopyFile simple definition} function FastCopyFile(const ASourceFileName, ADestinationFileName: TFileName): Boolean; overload; begin Result := FastCopyFile(ASourceFileName, ADestinationFileName, fcfmCreate); end; {FastCopyFile definition without any callbacks and default buffer} function FastCopyFile(const ASourceFileName, ADestinationFileName: TFileName; CopyMode: TFastCopyFileMode): Boolean; overload; begin Result := FastCopyFile(ASourceFileName, ADestinationFileName, CopyMode, DummyCallback, BufferSize_Default); end; {Definition with normal procedure callback and default buffer} function FastCopyFile(const ASourceFileName, ADestinationFileName: TFileName; CopyMode: TFastCopyFileMode; Callback: TFastCopyFileNormalCallback): Boolean; overload; begin Result := FastCopyFile(ASourceFileName, ADestinationFileName, CopyMode, DummyCallback, BufferSize_Default); end; {FastCopyFile definition with normal procedure callback and custom buffer} function FastCopyFile(const ASourceFileName, ADestinationFileName: TFileName; CopyMode: TFastCopyFileMode; Callback: TFastCopyFileNormalCallback; BufferSize: Cardinal) : Boolean; overload; var DummyObj: TDummyCallBackClient; begin DummyObj := TDummyCallBackClient.Create; try Result := FastCopyFile(ASourceFileName, ADestinationFileName, CopyMode, Callback, DummyObj.DummyCallback, BufferSize); finally DummyObj.Free; end; end; {FastCopyFile definition with object method callback and custom buffer} function FastCopyFile(const ASourceFileName, ADestinationFileName: TFileName; CopyMode: TFastCopyFileMode; Callback: TFastCopyFileMethodCallback; BufferSize: Cardinal) : Boolean; overload; begin Result := FastCopyFile(ASourceFileName, ADestinationFileName, CopyMode, DummyCallback, Callback, BufferSize); end; end.
Delphi-Quellcode:
Geht sicher noch schneller aber ich finde es gut soweit.
procedure FileCopyCallBack(const FileName: TFileName; const CurrentSize, TotalSize: Int64; var CanContinue: Boolean);
begin CanContinue := PruefeX_Y_Z; end; if FileSize < 4096 * 50 then BufferSize := iFileSize else BufferSize := 4096 * 50; if FastCopyFile(sSource, sDest, TFastCopyFileMode.fcfmCreate, FileCopyCallBack, BufferSize) then .... Original hier: ![]() |
AW: FileCopy im Thread
Ich würde ja immer Blockread Blockwrite nehmen... Oder eben Robocopy
Mavarik |
AW: FileCopy im Thread
Nur um es mal erwähnt zu haben.
Statt mit Threads kann man auch im selben Thread asynchron arbeiten. ![]() Kann sein, dass Windows/Dateisystemtreiber immer asynchron arbeiten und bei uns halt immer auf das ende warten, weil fast jeder synchron arbeitet. |
AW: FileCopy im Thread
Vielen Dank für eure Hinweise.
Ich denke, auch xcopy, robocopy und Kollegen kochen nur mit Wasser :wink: Ich habe jetzt etwas mehr Intelligenz in die Read/Write Threads verlagert und kann jetzt mit dem Code im Main-Thread leben.
Delphi-Quellcode:
while ( rest > 0 ) and ( Len > 0 ) do begin
if ( read1.xBuf1Ready = 1 ) and ( write1.xBuf1Ready = 0 ) and ( write2.xBuf1Ready = 0 ) then begin len := read1.xLen1Read; rest := rest - len; write1.xLen1Read := Len; write2.xLen1Read := Len; write1.xBuf1Ready := 1; // Start write 1 Buffer 1 write2.xBuf1Ready := 1; // Start write 2 Buffer 1 read1.xBuf1Ready := 2; // Buf1 wird geschrieben dbg( 'RUN rest ' + intToStr( ( Rest div 1024 div 1024 ) ) + 'MB Buffer ' + intToStr( ( Len div 1024 div 1024 ) ) + 'MB'); end; if ( read1.xBuf2Ready = 1 ) and ( write1.xBuf2Ready = 0 ) and ( write2.xBuf2Ready = 0 ) then begin len := read1.xLen2Read; rest := rest - len; write1.xLen2Read := Len; write2.xLen2Read := Len; write1.xBuf2Ready := 1; // Start write 1 Buffer 2 write2.xBuf2Ready := 1; // Start write 2 Buffer 2 read1.xBuf2Ready := 2; // Buf2 wird geschrieben dbg( 'RUN rest ' + intToStr( ( Rest div 1024 div 1024 ) ) + 'MB Buffer ' + intToStr( ( Len div 1024 div 1024 ) ) + 'MB'); end; if ( read1.xBuf1Ready = 2 ) and ( write1.xBuf1Ready = 0 ) and ( write2.xBuf1Ready = 0 ) then read1.xBuf1Ready := 0; // Freigabe Read Buffer 1 if ( read1.xBuf2Ready = 2 ) and ( write1.xBuf2Ready = 0 ) and ( write2.xBuf2Ready = 0 ) then read1.xBuf2Ready := 0; // Freigabe Read Buffer 2 sleep(1); end; // While end |
AW: FileCopy im Thread
Tipp:
![]() Ich hatte damals ein bissl mehr rumgeforscht, ein paar Dinge ausprobiert und versucht das kopieren zu optimieren. Tipp: Übergroß braucht man den Cache nicht zu machen, denn ALLE teilen das wieder auf (Dateisystemtreiber haben intern nicht unendlich viel Speicher und auch Netzwerkprotokolle haben für ihre einzelnen Datenpakete maximalgrenzen) ... aber es ist "oft" von Vorteil, wenn man ein "ganzes" Vielfaches der einzelnen Pakete verwendet. Vorallem sooo extrem große Dateien durch den WindowsFileCache zu jagen ist kontraproduktiv. (und standardmäßig machen das alle höheren Dateioperationen) |
Alle Zeitangaben in WEZ +1. Es ist jetzt 05:44 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz