Delphi szálkészlet-példa AsyncCalls használatával

Ez a következő tesztprojektem, hogy megvizsgáljam, hogy a Delphi szálak könyvtára melyik lenne a legmegfelelőbb „fájl szkennelés” feladatomhoz, amelyet több szálban / egy szálkészletben szeretnék feldolgozni.

A cél megismétlése: alakítsa át az 500–2000 fájlt tartalmazó szekvenciális „szkennelést” a nem menetes megközelítésről menetesre. Nem kellene egyszerre 500 szálat futnom, ezért szeretnék egy szálkészletet használni. A szálkészlet egy sorszerű osztály, amely számos futó szálat táplál a sorból következő feladattal.

Az első (nagyon alapvető) kísérlet a TThread osztály egyszerű kibővítésével és az Execute metódus (a menetes karakterlánc elemzőm) végrehajtásával történt.

Mivel a Delphi számára nem létezik egy szálkészlet osztály, amely a dobozból nem valósult meg, a második kísérletben megpróbáltam Primoz Gabrijelcic OmniThreadLibrary használatát.

Az OTL fantasztikus, zillion módon képes egy feladatot háttérben futtatni, és van egy módja annak, ha "tűz-és elfelejtés" megközelítést akar alkalmazni a kóddarab menetes végrehajtására.

instagram viewer

AsyncCalls: Andreas Hausladen

Megjegyzés: A következők könnyebben követhetők meg, ha először tölti le a forráskódot.

Míg további lehetőségeket vizsgáltam annak érdekében, hogy egyes funkcióimat menetes módon végrehajtsam, úgy döntöttem, hogy kipróbálom az "AsyncCalls.pas" egységet, amelyet Andreas Hausladen fejlesztett ki. Andy AsyncCalls - aszinkron funkcióhívások Az egység egy másik könyvtár, amelyet a Delphi fejlesztő felhasználhat annak érdekében, hogy enyhítse a menetes megközelítés végrehajtásának valamilyen kód végrehajtására.

Andy blogjából: Az AsyncCalls segítségével egyszerre több funkciót is végrehajthat, és szinkronizálhatja azokat a funkció vagy módszer minden pontján, amely elindította őket. Az AsyncCalls egység különféle funkciós prototípusokat kínál az aszinkron funkciók hívására. Végrehajt egy szálkészletet! A telepítés rendkívül egyszerű: csak használjon asynccalls-ot bármelyik egységéből, és azonnali hozzáféréssel rendelkezik olyan dolgokra, mint "végrehajt egy külön szálban, szinkronizálja a fő felhasználói felületet, várjon, amíg kész".

A szabadon használható (MPL licenc) AsyncCalls mellett Andy gyakran közzéteszi a Delphi IDE saját javításait, például "Delphi Speed ​​Up"és"DDevExtensions"Biztos vagyok benne, hogy hallottál róla (ha még nem használta).

AsyncCalls in Action

Lényegében az összes AsyncCall funkció visszatér egy IAsyncCall felületet, amely lehetővé teszi a funkciók szinkronizálását. Az IAsnycCall a következő módszereket teszi ki:

 //v. 2.98 az asynccalls.pas
IAsyncCall = interfész
// vár, amíg a funkció befejeződik, és visszatérési értéket ad vissza
funkció szinkronizálás: egész szám;
// visszatér True, ha az aszinkron funkció befejeződött
funkció kész: logikai;
// visszaadja az aszinkron funkció visszatérési értékét, ha a Kész érték IGAZ
ReturnValue függvény: egész szám;
// azt mondja az AsyncCalls-nak, hogy a hozzárendelt funkciót nem szabad végrehajtani az aktuális fenyegetésben
eljárás ForceDifferentThread;
végén;

Íme egy példa egy egész szám paramétert váró módszerre (IAsyncCall visszaadása):

 TAsyncCalls. Behívás (AsyncMethod, i, Random (500));
funkció TAsyncCallsForm. AsyncMethod (feladatNr, sleepTime: egész): egész;
kezdődik
eredmény: = sleepTime;
Alvás (sleepTime);
TAsyncCalls. VCLInvoke (
eljárás
kezdődik
Napló (Formátum ('kész> nem:% d / feladatok:% d / aludt:% d', [tasknr, asyncHelper. TaskCount, sleepTime]));
vég);
vég;

A TAsyncCalls. A VCLInvoke a szinkronizálás egyik módja a fő szállal (az alkalmazás fő szálával - az alkalmazás felhasználói felületével). A VCLInvoke azonnal visszatér. Az anonim módszert a fő szálban hajtjuk végre. Van még egy VCLSync, amely visszatér, amikor a főszálat meghívták az anonim módszert.

Menetkészlet az AsyncCalls-ban

Vissza a "fájlkeresés" feladatomhoz: az asynccalls szálkészlet adagolásakor (a for hurokban) a TAsyncCalls sorozattal. Invoke () hívások esetén a feladatok hozzáadódnak a készletbe, és "amikor eljön az idő" (amikor az előzőleg hozzáadott hívások befejeződtek) végrehajtásra kerülnek.

Várja meg az IAsyncCalls összes befejezését

Az asnycalls-ban megadott AsyncMultiSync függvény várja az async hívások (és más fogantyúk) befejezését. Van néhány túlterhelt az AsyncMultiSync hívásának módjai, és itt van a legegyszerűbb:

funkció AsyncMultiSync (const Lista: tömb IAsyncCall; WaitAll: logikai = igaz; Millisekundumok: bíboros = VÉGETT): bíboros; 

Ha azt akarom, hogy a "várjon mindent" megvalósításra kerüljön, töltsön ki egy IAsyncCall tömböt és elvégezze az AsyncMultiSync programot a 61-es szeletekben.

Saját AsnycCalls segítőm

Itt van egy darab a TAsyncCallsHelperről:

FIGYELMEZTETÉS: részleges kód! (a teljes kód letölthető)
felhasználások AsyncCalls;
típus
TIAsyncCallArray = tömb IAsyncCall;
TIAsyncCallArrays = tömb TIAsyncCallArray;
TAsyncCallsHelper = osztály
magán
fTasks: TIAsyncCallArrays;
ingatlan Feladatok: TIAsyncCallArrays olvas fTasks;
nyilvános
eljárás AddTask (const hívás: IAsyncCall);
eljárás WaitAll;
vég;
FIGYELMEZTETÉS: részleges kód!
eljárás TAsyncCallsHelper. WaitAll;
var
i: egész szám;
kezdődik
mert i: = Magas (Feladatok) le Alacsony (feladatok) csinál
kezdődik
AsyncCalls. AsyncMultiSync ([i] feladat);
vég;
vég;

Így "mindent megvárhatok" 61 darabonként (MAXIMUM_ASYNC_WAIT_OBJECTS) - vagyis várom az IAsyncCall tömbjeit.

A fentiekkel nézve a szálak készlete táplálására szolgáló fő kódom a következőképpen néz ki:

eljárás TAsyncCallsForm.btnAddTasksClick (Feladó: TObject);
const
nr elemek = 200;
var
i: egész szám;
kezdődik
asyncHelper. MaxThreads: = 2 * rendszer. CPUCount;
ClearLog ( 'kiindulási');
mert i: = 1-ig nr csinál
kezdődik
asyncHelper. AddTask (TAsyncCalls. Invoke (AsyncMethod, i, Random (500));
vég;
Napló ('all in');
// várjon mindent
//asyncHelper.WaitAll;
// vagy engedélyezheti a még nem kezdődő összes esemény törlését a „Mindent töröl” gombra kattintva:

míg NEM asyncHelper. Befejezve csinál Alkalmazás. ProcessMessages;
Log ( 'kész');
vég;

Mindent törölni? - Meg kell változtatni az AsyncCalls.pas-t :(

Azt is szeretném, ha lenne módja „törölni” azokat a feladatokat, amelyek a medencében vannak, de várják végrehajtásukat.

Sajnos az AsyncCalls.pas nem biztosítja a feladat törlésének egyszerű módját, miután hozzáadta a szálkészlethez. Nincs IAsyncCall. Mégse vagy IAsyncCall. DontDoIfNotAlreadyExecuting vagy IAsyncCall. NeverMindMe.

Ahhoz, hogy ez működjön, meg kellett változtatnom az AsyncCalls.pas-t úgy, hogy megpróbáltam megváltoztatni a lehető legkevesebbel - tehát hogy amikor Andy kiad egy új verziót, csak néhány sort kell hozzátennem, hogy megkapjam a "Feladat megszakítása" ötletet dolgozó.

Íme, amit tettem: hozzáadtam egy "Mégsem" eljárást az IAsyncCall-hoz. A Mégsem eljárás beállítja az "FCancelled" (hozzáadott) mezőt, amelyet ellenőrizni kell, amikor a készlet készen áll a feladat végrehajtására. Kicsit meg kellett változtatnom az IAsyncCall-t. Befejeződött (úgy, hogy a hívásjelentések akkor is befejeződjenek, ha megszakítják) és a TAsyncCall. InternExecuteAsyncCall eljárás (a hívás nem hajtható végre, ha azt megszakították).

Te tudod használni WinMerge hogy könnyen megtaláljam a különbségeket Andy eredeti asynccall.pas és a megváltozott verzióm között (a letöltésben is).

Letöltheti a teljes forráskódot, és felfedezheti.

Gyónás

ÉRTESÍTÉS! :)

Az CancelInvocation A módszer megakadályozza az AsyncCall meghívását. Ha az AsyncCall már feldolgozva van, akkor a CancelInvocation hívására nincs hatása, és a Törölt funkció hamis lesz, mivel az AsyncCall nem lett visszavonva.
Az Törölve A módszer igaznak bizonyul, ha az AsyncCall-ot a CancelInvocation törölte.
Az Elfelejt módszer lekapcsolja az IAsyncCall felületet a belső AsyncCallról. Ez azt jelenti, hogy ha az IAsyncCall felületre utaló utolsó hivatkozás eltűnik, akkor az aszinkron hívás továbbra is végrehajtásra kerül. Az interfész módszerei kivételt jelentenek, ha a Felejtés hívása után hívják fel. Az async függvény nem hívhatja be a fonalat, mert a TThread után végrehajtható. A szinkronizálás / várólistát az RTL leállította, ami holt zárolást okozhat.

Vegye figyelembe, hogy továbbra is élvezheti az AsyncCallsHelper szolgáltatásait, ha meg kell várnia, hogy az összes async-hívás befejeződik az "asyncHelper" -vel. WaitAll "; vagy ha "CancelAll" -re van szüksége.

instagram story viewer