Toto je môj ďalší testovací projekt, aby som zistil, ktorá knižnica vlákien pre spoločnosť Delphi by ma najlepšie vyhovovala pre moju úlohu „skenovania súborov“, ktorú by som chcel spracovať vo viacerých vláknach / v oblasti vlákien.
Ak chcete zopakovať môj cieľ: transformujte moje sekvenčné „skenovanie“ súborov 500 - 2 000 + súborov z prístupu bez vlákien na prístup bez vlákien. Nemal by som mať naraz spustených 500 vlákien, preto by som chcel použiť oblasť vlákien. Oblasť vlákien je triedou podobnou frontu, ktorá dodáva množstvu spustených vlákien ďalšej úlohe z frontu.
Prvý (veľmi jednoduchý) pokus sa uskutočnil jednoduchým rozšírením triedy TThread a implementáciou metódy Execute (môj analyzátor vláknových reťazcov).
Pretože spoločnosť Delphi nemá implementovanú triedu podprocesov po vybalení z krabice, v mojom druhom pokuse som sa pokúsila použiť OmniThreadLibrary od Primoz Gabrijelcic.
OTL je fantastický, má milión spôsobov, ako spustiť úlohu na pozadí, spôsob, ako ísť, ak chcete mať prístup „ohňom a zabudnite“ k odovzdávaniu závitových vykonaní kusov kódu.
AsyncCalls Andreas Hausladen
Poznámka: Nasledujúce kroky by bolo ľahšie sledovať, ak si prvýkrát stiahnete zdrojový kód.
Pri skúmaní viacerých spôsobov, ako nechať niektoré z mojich funkcií vykonávať spôsobom so závitom, som sa rozhodol vyskúšať aj jednotku „AsyncCalls.pas“, ktorú vyvinula Andreas Hausladen. andy AsyncCalls - Asynchrónne volania funkcií jednotka je ďalšia knižnica, ktorú vývojár spoločnosti Delphi môže použiť na zmiernenie bolesti pri implementácii vláknového prístupu k vykonávaniu nejakého kódu.
Z blogu Andyho: S AsyncCalls môžete vykonávať viac funkcií súčasne a synchronizovať ich v každom bode funkcie alebo metódy, ktorá ich spustila... Jednotka AsyncCalls ponúka množstvo prototypov funkcií na volanie asynchrónnych funkcií... Implementuje vlákno pool! Inštalácia je super jednoduchá: stačí použiť asynchrónne hovory z ktorejkoľvek z vašich jednotiek a máte okamžitý prístup k veciam ako „spustiť v samostatnom vlákne, synchronizovať hlavné používateľské rozhranie, počkať, kým sa nedokončí“.
Okrem bezplatného používania (licencia MPL) AsyncCalls, Andy tiež často publikuje svoje vlastné opravy pre Delphi IDE ako „Zrýchlenie Delphi„a“DDevExtensions„Určite ste už počuli (ak už nepoužívate).
AsyncCalls in Action
V podstate všetky funkcie AsyncCall vracajú rozhranie IAsyncCall, ktoré umožňuje synchronizáciu funkcií. IAsnycCall vystavuje nasledujúce metódy:
//v 2,98 na asynccalls.pas
IAsyncCall = rozhranie
// čaká na dokončenie funkcie a vráti návratovú hodnotu
funkcia Sync: Celé číslo;
// vráti True po dokončení asynchrónnej funkcie
funkcia Dokončené: Boolean;
// vráti návratovú hodnotu asynchrónnej funkcie, keď je Dokončené TRUE
funkcia ReturnValue: Integer;
// hovorí AsyncCalls, že priradená funkcia sa nesmie vykonať v aktuálnom vlákne
postup ForceDifferentThread;
koniec;
Tu je príklad volania metódy očakávajúcej dva celočíselné parametre (vrátenie IAsyncCall):
TAsyncCalls. Vyvolanie (AsyncMethod, i, Random (500));
funkcie TAsyncCallsForm. AsyncMethod (taskNr, sleepTime: integer): integer;
začať
result: = sleepTime;
Spánok (sleepTime);
TAsyncCalls. VCLInvoke (
procedúra
začať
Log (Format ('done> nr:% d / task:% d / sleept:% d', [tasknr, asyncHelper. TaskCount, sleepTime]);
koniec);
koniec;
TAsyncCalls. VCLInvoke je spôsob synchronizácie s hlavným vláknom (hlavné vlákno aplikácie - používateľské rozhranie aplikácie). VCLInvoke sa okamžite vráti. Anonymná metóda bude vykonaná v hlavnom vlákne. K dispozícii je tiež VCLSync, ktorý sa vracia, keď bola v hlavnom vlákne volaná anonymná metóda.
Podproces vlákno v AsyncCalls
Späť na moju úlohu „skenovania súborov“: pri napájaní (v cykle for)) asynccalls vlákno pool s radom TAsyncCalls. Hovormi Invoke () sa úlohy pridajú do vnútornej oblasti a vykonajú sa „keď príde čas“ (keď sa predtým pridané hovory skončia).
Počkajte na dokončenie všetkých IAsyncCalls
Funkcia AsyncMultiSync definovaná v programe asnyccalls čaká na dokončenie asynchrónnych hovorov (a iných úchytiek). Je tam niekoľko preťažený spôsoby, ako volať na AsyncMultiSync, a tu je najjednoduchší:
funkcie AsyncMultiSync (const list: rad IAsyncCall; WaitAll: Boolean = True; Milisekundy: kardinál = INFINITE): kardinál;
Ak chcem mať implementáciu „počkajte všetko“, musím vyplniť pole IAsyncCall a urobiť asyncMultiSync v rezoch 61.
Môj pomocník AsnycCalls
Tu je kúsok pomocníka TAsyncCalls:
VAROVANIE: čiastočný kód! (celý kód je k dispozícii na stiahnutie)
použitie AsyncCalls;
typ
TIAsyncCallArray = rad IAsyncCall;
TIAsyncCallArrays = rad TIAsyncCallArray;
TAsyncCallsHelper = trieda
súkromné
fTasks: TIAsyncCallArrays;
nehnuteľnosť Úlohy: TIAsyncCallArrays čítať fTasks;
verejnosť
procedúra AddTask (const volajte: IAsyncCall);
procedúra WaitAll;
koniec;
VAROVANIE: čiastočný kód!
procedúra TAsyncCallsHelper. WaitAll;
var
i: celé číslo;
začať
pre i: = Vysoká (Úlohy) downto Nízka (Úlohy) robiť
začať
AsyncCalls. AsyncMultiSync (Úlohy [i]);
koniec;
koniec;
Týmto spôsobom môžem „čakať všetko“ v kuse 61 (MAXIMUM_ASYNC_WAIT_OBJECTS) - t. J. Čakať na polia IAsyncCall.
Podľa vyššie uvedeného vyzerá môj hlavný kód na kŕmenie oblasti vlákien:
procedúra TAsyncCallsForm.btnAddTasksClick (Sender: TObject);
const
nrItems = 200;
var
i: celé číslo;
začať
asyncHelper. MaxThreads: = 2 * systém. CPUCount;
ClearLog ( 'predvolená');
pre i: = 1 na číslo robiť
začať
asyncHelper. AddTask (TAsyncCalls. Vyvolanie (AsyncMethod, i, Random (500)));
koniec;
Denník („všetko v“);
// počkaj všetky
//asyncHelper.WaitAll;
// alebo povoliť zrušenie všetkých nezačatých kliknutím na tlačidlo „Zrušiť všetko“:
zatiaľ čo NIE asyncHelper. AllFinished robiť Aplikácie. ProcessMessages;
Log ( 'hotové');
koniec;
Zrušiť všetko? - Musia zmeniť AsyncCalls.pas :(
Chcel by som mať tiež spôsob, ako „zrušiť“ tie úlohy, ktoré sú v bazéne, ale čakajú na ich vykonanie.
Bohužiaľ, AsyncCalls.pas neposkytuje jednoduchý spôsob zrušenia úlohy po jej pridaní do oblasti vlákien. Neexistuje žiadny IAsyncCall. Zrušiť alebo IAsyncCall. DontDoIfNotAlreadyExecuting alebo IAsyncCall. Nevermind.
Aby to fungovalo, musel som zmeniť AsyncCalls.pas tým, že som sa snažil zmeniť to čo najmenej - tak že keď Andy vydá novú verziu, musím pridať iba niekoľko riadkov, aby som mal nápad „Zrušiť úlohu“ pracovať.
Tu je to, čo som urobil: Do IAsyncCall som pridal postup „Zrušenie procedúry“. Procedúra Cancel nastavuje pole „FCancelled“ (pridané), ktoré sa skontroluje, keď sa fond chystá začať vykonávať úlohu. Potreboval som mierne zmeniť IAsyncCall. Dokončené (aby sa hlásenia o hovoroch skončili, aj keď boli zrušené) a TAsyncCall. Postup InternExecuteAsyncCall (nevykonať hovor, ak bol zrušený).
Môžeš použiť WinMerge aby ste ľahko našli rozdiely medzi pôvodnou verziou Andyho asynccall.pas a mojou zmenenou verziou (zahrnuté v stiahnutí).
Môžete si stiahnuť celý zdrojový kód a preskúmať.
vyznanie
UPOZORNENIE! :)
CancelInvocation metóda zastaví spustenie AsyncCall. Ak je AsyncCall už spracovaný, volanie na CancelInvocation nemá žiadny účinok a funkcia Canceled vráti False, pretože AsyncCall nebol zrušený.
zrušený metóda vracia true, ak bol AsyncCall zrušený pomocou CancelInvocation.
zabudnúť metóda odpojí rozhranie IAsyncCall od interného AsyncCall. To znamená, že ak je posledný odkaz na rozhranie IAsyncCall preč, asynchrónne volanie sa bude stále vykonávať. Metódy rozhrania vyvolajú výnimku, ak budú volané po volaní zabudnutia. Async funkcia nesmie volať do hlavného vlákna, pretože mohla byť vykonaná po TThread. Mechanizmus synchronizácie / poradia bol zrušený RTL, čo môže spôsobiť mŕtvy zámok.
Nezabúdajte však, že z môjho AsyncCallsHelper môžete mať úžitok, ak potrebujete čakať na ukončenie všetkých asynchrónnych hovorov pomocou „asyncHelper“. WaitAll "; alebo ak potrebujete „Zrušiť všetko“.