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.
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.