08. november 2010 - 09:04Der er
9 kommentarer og 1 løsning
Korrekt og sikker maade at stoppe flere threads?
Hej,
som diskuteret tidligere har jeg et program, som bruger flere instanser af en traad (dvs. flere traade af samme art creates). Traadene kopiere forskellige filer fra hver sin mappe til en anden:
Traad 1: Filer fra Mappe1 (lokal) til Mappe11 (netvaerk)
Traad 2: Filer fra Mappe2 (lokal) til Mappe2233 (netvaerk)
Traad 3: Filer fra Mappe3 (lokal) til Mappe2233 (netvaerk)
Hvis jeg nu fra min hovedtraad opretter nye filer i mapperne 1-3, stopper jeg foerst alle tre traade, for at vaere sikker paa, de ikke kopierer noget, mens jeg opretter nye filer:
if FThread1 <> NIL then FThread1.Suspend; if FThread2 <> NIL then FThread2.Suspend; if FThread3 <> NIL then FThread3.Suspend;
Dette faar NOGEN GANGE en (eller flere?) af traadene til at smide en fejl, men ikke hver gang:
Thread-error: Access in denied (5)
Der ER rettigheder (og adgang) til alle mapper, saa kunne det vaere noget med, at traadene kolliderer?
Her er execute-delen af traadene:
procedure TCopyThread.Execute; const PowerNap = 100; //ms. var Found: Bool; WakeUp: cardinal; SearchRec: TSearchRec; begin while not Terminated and not Suspended do begin if DirectoryExists(ExcludeTrailingPathDelimiter(FSourcePath)) and DirectoryExists(ExcludeTrailingPathDelimiter(FDestPath)) then begin Found := FindFirst(FSourcePath + '*.xml', faAnyFile or faReadOnly, SearchRec) = 0; try while Found and not Terminated do begin if CopyFile(PChar(FSourcePath + SearchRec.Name), PChar(FDestPath + SearchRec.Name), False) then //False causes CopyFile to overwrite an existing file. if FDeleteAfterCopy then DeleteFile(PChar(FSourcePath + SearchRec.Name)); Found := FindNext(SearchRec) = 0; end; finally FindClose(SearchRec); end; end;
WakeUp := GetTickCount + FInterval; //Run maximum every xx ms. while not Terminated and (WakeUp > GetTickCount) do Sleep(PowerNap); end; end;
Måske skal du sikre dig at diskoperationerne er færdige før du suspender trådene? Noget lignende WaitFor når trådene skal terminere.
Du har også muligheden at lade trådene reagere på de ændringer der sker i katalogerne. Altså selv opdage når der kommer nye filer. Du sletter jo aldrig, vel?
Jeg sletter filerne, naar de er blevet kopieret. Hvilke funktioner skal jeg kigge paa ang. "Du har også muligheden at lade trådene reagere på de ændringer der sker i katalogerne."
Det kunne være interessant at se hvilken fil og operation tråden var i gang med da den frembragte fejlen. Det kunne måske være at den faktisk er ved at flytte den fil du er i gang med at lave, betydende at suspend ikke er helt OK. Jeg plejer at bruge suspended := true da jeg vistnok havde problemer med suspend og resume
Skummelt med de traade! Fejlen ser ud til at komme, naar netvaerket et oejeblik ikke er tilgaengeligt. Det ved jeg kan ske, hvilket ogsaa er derfor jeg har valgt at laegge kopieringen separat, saa der bare forsoeges igen senere ved fejl. At det saa resulterede i en fejlmelding, var jo ikke meningen.
>>while Found and not Terminated do >>( du burde nok også checke på suspended her )
Det fjernede ikke fejlen, men jeg implementerede det alligevel, tak.
Det, som ser ud til at virke er at kapsle hele baduljen ind i en try/except:
procedure TCopyThread.Execute; const PowerNap = 100; //ms. var Found: Bool; WakeUp: Cardinal; SearchRec: TSearchRec; begin while not Terminated and not Suspended do begin try Found := (FindFirst(FSourcePath + '*' + FFileExtension, faAnyFile or faReadOnly, SearchRec) = 0); //Check if any new files to be copied. try if Found then if DirectoryExists(GetDestPath(FSourcePath + SearchRec.Name)) then while Found and not Terminated and not Suspended do begin if CopyFile(PChar(FSourcePath + SearchRec.Name), PChar(IncludeTrailingPathDelimiter(GetDestPath(FSourcePath + SearchRec.Name)) + SearchRec.Name), False) then //False causes CopyFile to overwrite an existing file. if FDeleteAfterCopy then begin //Delete file and header file: if DeleteFile(PChar(FSourcePath + SearchRec.Name)) then DeleteFile(PChar(FSourcePath + StringReplace(SearchRec.Name, ExtractFileExt(SearchRec.Name), '.h', []))); end; Found := (FindNext(SearchRec) = 0); end; finally FindClose(SearchRec); end; except ;//Exceptions are ignored. end;
WakeUp := GetTickCount + FInterval; //Run maximum every xx ms. while not Terminated and (WakeUp > GetTickCount) do Sleep(PowerNap);
end; end;
Jeg troede egentlig at try/finally ogsaa opfangede exceptions, men jeg er blevet klogere.
Ioevrigt maatte jeg aendre min procedure for at stoppe traadene, da de froes, hvis jeg suspendede dem to gange!!:
procedure TfrmMain.StopThreads(); begin if (FCopyThreadLogs <> NIL) and not FCopyThreadLogs.Suspended then FCopyThreadLogs.Suspend; if (FCopyThreadTests <> NIL) and not FCopyThreadTests.Suspended then FCopyThreadTests.Suspend; if (FMergeThreadMonitorLogs <> NIL) and not FMergeThreadMonitorLogs.Suspended then FMergeThreadMonitorLogs.Suspend; end;
Spoergsmaal: Kan man altid regne med at try/except opfanger en exception, selv hvis fejlen opstaar i en kaldt procedure (hvor kaldet er placeret i try/except, men ingen try/except i selve den kaldte procedure)?
Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.