Primjer baze podataka nitnih dijelova Delphi pomoću AsyncCalls

Ovo je moj sljedeći testni projekt da vidim koja bi knjižnica za obradu datoteka za Delphi najbolje odgovarala za moj zadatak "skeniranja datoteka" koji bih želio obraditi u više niti / u nizu niti.

Da ponovim svoj cilj: transformirajte svoje sekvencijalno "skeniranje datoteka" od 500-2000 + datoteka iz pristupa bez navoja u jedan s navojem. Ne bih trebao istovremeno pokrenuti 500 niti, želio bih upotrijebiti skup niti. Nit pojma je klasa nalik na red koja sadrži brojne pokrenute niti sa sljedećim zadatkom iz reda.

Prvi (vrlo osnovni) pokušaj napravljen je jednostavnim proširenjem klase TThread i primjenom metode Execute (moj raščlanjeni niz niti).

Budući da Delphi nema klasu navoja niti koja je implementirana izvan okvira, u svom drugom pokušaju pokušao sam koristiti OmniThreadLibrary Primoza Gabrijelčića.

OTL je fantastičan, ima milijun načina za izvršavanje zadatka u pozadini, put kojim želite pristupiti "vatri i zaboravi" za predavanje navođenih izvršenja komada vašeg koda.

AsyncCalls Andreas Hausladen

instagram viewer
Napomena: Ono što slijedi bilo bi jednostavnije pratiti ako prvi put preuzmete izvorni kod.

Istražujući više načina da se neke od mojih funkcija izvršavaju na navojni način, odlučio sam isprobati i jedinicu "AsyncCalls.pas" koju je razvio Andreas Hausladen. Andy AsyncCalls - Asinhroni pozivi funkcija jedinica je još jedna biblioteka koju Delphi programer može upotrijebiti za olakšavanje boli u implementaciji navojanog pristupa u izvršavanju nekog koda.

S Andyjevog bloga: Pomoću AsyncCalls možete istovremeno izvršavati više funkcija i sinkronizirati ih u svakoj točki funkcije ili metode koji ih je pokrenuo... AsyncCalls jedinica nudi mnoštvo prototipa funkcija za pozivanje asinkronih funkcija... Provodi bazen navoja! Instalacija je vrlo jednostavna: samo koristite asynccalls iz bilo koje jedinice i imat ćete trenutni pristup stvarima poput "izvršiti u zasebnoj niti, sinkronizirati glavno korisničko sučelje, pričekati dok ne završite".

Osim slobodnog korištenja (MPL licenca) AsyncCalls, Andy često objavljuje i vlastite popravke za Delphi IDE poput "Delphi ubrzava"i"DDevExtensions"Sigurna sam da ste čuli (ako već ne koristite).

AsyncCalls u akciji

U biti, sve funkcije AsyncCall vraćaju IAsyncCall sučelje koje omogućuje sinkronizaciju funkcija. IAsnycCall izlaže sljedeće metode:

 //v 2,98 od asynccalls.pas
IAsyncCall = sučelje
// čeka dok se funkcija završi i vrati povratnu vrijednost
funkcija Sync: Integer;
// vraća True kad je funkcija asinkrone završena
funkcija Završena: Boolean;
// vraća povratnu vrijednost funkcije asinkrone, kada je Finished (TRUE) TRUE
funkcija ReturnValue: Integer;
// kaže AsyncCalls da se dodijeljena funkcija ne smije izvršiti u trenutnoj prijetnji
postupak ForceDifferentThread;
kraj;

Evo primjera poziva metode koja očekuje dva cjelobrojna parametra (vraćanje IAsyncCall):

 TAsyncCalls. Invoke (AsyncMethod, i, Random (500));
funkcija TAsyncCallsForm. AsyncMethod (taskNr, sleepTime: integer): cijeli broj;
početi
rezultat: = vrijeme spavanja;
Spavanje (vrijeme spavanja);
TAsyncCalls. VCLInvoke (
postupak
početi
Prijavite se (Format ('učinjeno> nr:% d / zadatci:% d / spavao:% d', [tasknr, asyncHelper). TaskCount, sleepTime]));
kraj);
kraj;

TAsyncCalls. VCLInvoke je način za sinkronizaciju s vašim glavnim nitom (glavna nit aplikacije - korisničko sučelje vaše aplikacije). VCLInvoke se odmah vraća. Anonimna metoda izvršava se u glavnoj niti. Tu je i VCLSync koji se vraća kad se u glavnu nit pozvala anonimna metoda.

Bazen navoja u AsyncCalls

Povratak na moj zadatak "skeniranja datoteka": kada hranite (u petlji), niz niti asynccalls s nizom TAsyncCalls. Pozovite () pozive, zadaci će se dodati u interni bazen i izvršavat će se "kad dođe vrijeme" (kada su prethodno dodani pozivi završeni).

Pričekajte da se svi IAsyncCalls završe

AsyncMultiSync funkcija definirana u asnyccallima čeka da se async pozivi (i ostale ručke) završe. Postoji nekoliko preopterećen načina za pozivanje AsyncMultiSync, a evo najjednostavnijeg:

funkcija AsyncMultiSync (const Popis: niz od IAsyncCall; Čekaj sve: Boolean = Istina; Milisekunde: kardinal = INFINITE): kardinal; 

Ako želim implementirati "pričekaj sve", moram ispuniti niz IAsyncCall i napraviti AsyncMultiSync u kriške od 61.

Moj AsnycCalls pomoćnik

Evo dijela TAsyncCallsHelper-a:

UPOZORENJE: djelomični kod! (puni kod dostupan za preuzimanje)
namjene AsyncCalls;
tip
TIAsyncCallArray = niz od IAsyncCall;
TIAsyncCallArrays = niz od TIAsyncCallArray;
TAsyncCallsHelper = klasa
privatna
fZadaci: TIAsyncCallArrays;
svojstvo Zadaci: TIAsyncCallArrays čitati fTasks;
javnost
postupak AddTask (const poziv: IAsyncCall);
postupak WaitAll;
kraj;
UPOZORENJE: djelomični kod!
postupak TAsyncCallsHelper. WaitAll;
var
i: cijeli broj;
početi
za i: = visoko (zadaci) do Niska (zadaci) čini
početi
AsyncCalls. AsyncMultiSync (Zadaci [i]);
kraj;
kraj;

Na ovaj način mogu "čekati sve" u komadima od 61 (MAXIMUM_ASYNC_WAIT_OBJECTS) - tj. Čekajući niz IAsyncCall.

S gore navedenim, moj glavni kod za punjenje niza izgleda ovako:

postupak TAsyncCallsForm.btnAddTasksClick (Pošiljatelj: TObject);
const
nrItems = 200;
var
i: cijeli broj;
početi
asyncHelper. MaxThreads: = 2 * Sustav. CPUCount;
ClearLog ( 'početni');
za i: = 1 do nrItems čini
početi
asyncHelper. AddTask (TAsyncCalls). Invoke (AsyncMethod, i, Random (500)));
kraj;
Prijavite se ("sve u");
// čekati sve
//asyncHelper.WaitAll;
// ili dopustiti otkazivanje svega što nije započelo klikom na gumb "Otkaži sve":

dok NE asyncHelper. AllFinished čini Primjena. ProcessMessages;
Log ( 'gotovog');
kraj;

Otkazati sve? - Morate promijeniti AsyncCalls.pas :(

Također bih želio način "otkazati" one zadatke koji se nalaze u bazenu, ali čekaju na njihovo izvršavanje.

Nažalost, AsyncCalls.pas ne pruža jednostavan način otkazivanja zadatka nakon što je dodan u niz niti. Ne postoji IAsyncCall. Otkaži ili IAsyncCall. DontDoIfNotAlreadyExecuting ili IAsyncCall. NeverMindMe.

Da bi ovo uspjelo, morao sam promijeniti AsyncCalls.pas nastojeći je izmijeniti što je manje moguće - dakle kad Andy izda novu verziju, moram dodati samo nekoliko redaka kako bih imao svoju ideju "Otkaži zadatak" radi.

Evo što sam učinio: IAsyncCall dodao sam "postupak odustajanja". Postupak odustajanja postavlja polje "FCancelated" (dodano) koje se provjerava kada se bazen sprema za izvršavanje zadatka. Trebao sam malo izmijeniti IAsyncCall. Gotovo (tako da su izvješća o pozivima završena i kad su otkazana) i TAsyncCall. InternExecuteAsyncCall postupak (ne izvršava poziv ako je otkazan).

Možeš koristiti winmerge da se lako pronađu razlike između Andyjevog izvornog asynccall.pasa i moje izmijenjene verzije (uključeno u preuzimanju).

Možete preuzeti puni izvorni kod i istražiti.

ispovijest

OBAVIJEST! :)

 CancelInvocation metoda sprečava pozivanje AsyncCall-a. Ako je AsyncCall već obrađen, poziv na CancelInvocation nema učinka i Otkazana funkcija vratit će False jer AsyncCall nije otkazan.
otkazan vraća metodu True ako je AsyncCall otkazao CancelInvocation.
Zaboraviti metoda uklanja vezu IAsyncCall sučelja s unutarnjeg AsyncCall. To znači da ako nestane i posljednja referenca na IAsyncCall sučelje, asinhroni poziv će se i dalje izvršiti. Metode sučelja će izuzeti iznimku ako se nazove nakon poziva Forget. Async funkcija ne smije ulaziti u glavni nit jer se može izvršiti nakon TThreada. Mehanizam sinkronizacije / čekanja zaustavio je RTL što može uzrokovati mrtvu bravu.

Međutim, imajte na umu da i dalje možete imati koristi od mog AsyncCallsHelper ako trebate pričekati da se svi pozivi async završe s "asyncHelper. WaitAll "; ili ako trebate "CancelAll".