Delphi pavedienu baseina piemērs, izmantojot AsyncCalls

Šis ir mans nākamais testa projekts, lai redzētu, kura vītņu bibliotēka Delphi būtu piemērota manam “failu skenēšanas” uzdevumam, kuru es vēlētos apstrādāt vairākos pavedienos / pavedienu baseinā.

Lai atkārtotu savu mērķi: pārveidojiet manu secīgo “failu skenēšanu” no 500 līdz 2000 failiem no vītņotas pieejas uz vītņotu. Man nevajadzētu vienā reizē darbināt 500 pavedienus, tāpēc es vēlētos izmantot diegu baseinu. Vītņu fonds ir rindai līdzīga klase, kas baro vairākus tekošos pavedienus ar nākamo uzdevumu no rindas.

Pirmais (ļoti pamata) mēģinājums tika veikts, vienkārši paplašinot TThread klasi un īstenojot metodi Execute (mana vītņoto virkņu parsētājs).

Tā kā Delphi nav pavedienu kopas klases, kas būtu ieviesta ārpus lodziņa, otrajā mēģinājumā esmu mēģinājis izmantot Primoz Gabrijelcic OmniThreadLibrary.

OTL ir fantastisks, tam ir ziljons veidu, kā izpildīt uzdevumu fonā, veids, kā iet, ja vēlaties izmantot “ugunsgrēku un aizmirst” pieeju, lai nodotu sava koda daļu vītņotu izpildi.

Andreas Hausladena veidotais AsyncCalls

instagram viewer
Piezīme: kas aprakstīts tālāk, būs vieglāk ievērot, ja pirmo reizi lejupielādēsit avota kodu.

Izpētot citus veidus, kā dažas manas funkcijas izpildīt vītņotā veidā, esmu nolēmis izmēģināt arī Andreas Hausladen izstrādāto vienību “AsyncCalls.pas”. Andy's AsyncCalls - asinhrono funkciju izsaukumi vienība ir vēl viena bibliotēka, kuru Delphi izstrādātājs var izmantot, lai atvieglotu sāpes, kas saistītas ar vītņotas pieejas ieviešanu kāda koda izpildei.

No Andy emuāra: Ar AsyncCalls jūs varat vienlaikus izpildīt vairākas funkcijas un sinhronizēt tās visos funkcijas vai metodes punktos, kas tās sāka. AsyncCalls vienība piedāvā dažādus funkciju prototipus, lai izsauktu asinhronās funkcijas... Tas ievieš diegu baseinu! Instalēšana ir ļoti vienkārša: vienkārši izmantojiet asynccalls no jebkuras savas vienības un jums ir tūlītēja piekļuve tādām lietām kā "izpildīt atsevišķā pavedienā, sinhronizēt galveno lietotāja saskarni, pagaidiet, līdz pabeigta".

Blakus bezmaksas lietojumam (MPL licence) AsyncCalls, Andijs arī bieži publicē savus Delphi IDE labojumus, piemēram, “Delphi Speed ​​Up" un "DDevExtensions"Esmu pārliecināts, ka esat par to dzirdējis (ja to jau nelietojat).

AsyncCalls darbībā

Būtībā visas AsyncCall funkcijas atgriež IAsyncCall saskarni, kas ļauj sinhronizēt funkcijas. IAsnycCall pakļauj šādas metodes:

 //v. 2.98 no asynccalls.pas
IAsyncCall = saskarne
// gaida, līdz funkcija ir pabeigta, un atgriež atgriešanās vērtību
funkcija Sinhronizēt: vesels skaitlis;
// atgriež True, kad asinhronā funkcija ir pabeigta
funkcija Pabeigta: Būla;
// atgriež asinhronās funkcijas atgriešanās vērtību, kad Pabeigts ir PATIESA
funkcija ReturnValue: vesels skaitlis;
// stāsta AsyncCalls, ka piešķirto funkciju nedrīkst izpildīt pašreizējos draudos
procedūra ForceDifferentThread;
beigas;

Šeit ir piemērs, kas izsauc metodi, kurā tiek gaidīti divi veseli skaitļu parametri (IAsyncCall atdošana):

 TAsyncCalls. Izsaukt (AsyncMethod, i, Random (500));
funkcija TAsyncCallsForm. AsyncMethod (uzdevuma nr., Miega laiks: vesels skaitlis): vesels skaitlis;
sākt
rezultāts: = sleepTime;
Miega režīms (sleepTime);
TAsyncCalls. VCLInvoke (
procedūra
sākt
Žurnāls (Formāts ('veikts> nr.:% D / uzdevumi:% d / gulējis:% d', [tasknr, asyncHelper. TaskCount, sleepTime]));
beigas);
beigas;

TAsyncCalls. VCLInvoke ir veids, kā veikt sinhronizāciju ar galveno pavedienu (lietojumprogrammas galvenais pavediens - jūsu lietojumprogrammas lietotāja interfeiss). VCLInvoke atgriežas nekavējoties. Anonīma metode tiks izpildīta galvenajā pavedienā. Ir arī VCLSync, kas atgriežas, kad galvenajā pavedienā tiek izsaukta anonīma metode.

Vītņu baseins AsyncCalls

Atpakaļ pie mana "failu skenēšanas" uzdevuma: barojot (for for loop), asinhronu pavedienu pūlu ar TAsyncCalls sērijām. Invoke () zvani, uzdevumi tiks pievienoti baseina iekšienē un tiks izpildīti "kad pienāks laiks" (kad iepriekš pievienotie zvani būs pabeigti).

Pagaidiet visus IAsyncCalls

AsyncMultiSync funkcija, kas definēta asnyccalls, gaida async zvanu (un citu rokturu) pabeigšanu. Ir daži pārslogots AsyncMultiSync izsaukšanas veidi, un tas ir vienkāršākais:

funkcija AsyncMultiSync (const Saraksts: masīvs IAsyncCall; WaitAll: Būla = patiesa; Milisekundes: kardināls = INFINITE): kardināls; 

Ja es vēlos, lai tiktu “pagaidīts viss”, man jāaizpilda IAsyncCall masīvs un AsyncMultiSync jāveic 61 šķēlēs.

Mans AsnycCalls palīgs

Šis ir TAsyncCallsHelper gabals:

BRĪDINĀJUMS: daļējs kods! (pilns kods pieejams lejupielādei)
izmanto AsyncCalls;
tips
TIAsyncCallArray = masīvs IAsyncCall;
TIAsyncCallArrays = masīvs TIAsyncCallArray;
TAsyncCallsHelper = klase
Privāts
fTasks: TIAsyncCallArrays;
īpašums Uzdevumi: TIAsyncCallArrays lasīt fTasks;
publiski
procedūra Pievienojumprogramma (const zvans: IAsyncCall);
procedūra Pagaidiet visu;
beigas;
BRĪDINĀJUMS: daļējs kods!
procedūra TAsyncCallsHelper. Pagaidiet visu;
var
i: vesels skaitlis;
sākt
priekš i: = augsts (uzdevumi) līdz Zems (uzdevumi) darīt
sākt
AsyncCalls. AsyncMultiSync (uzdevumi [i]);
beigas;
beigas;

Tādā veidā es varu "gaidīt visu" gabalos 61 (MAXIMUM_ASYNC_WAIT_OBJECTS) - t.i., gaidīt IAsyncCall masīvus.

Ņemot vērā iepriekš minēto, mans galvenais kods pavedienu pūla padevei izskatās šādi:

procedūra TAsyncCallsForm.btnAddTasksClick (Sūtītājs: TObject);
const
nrItems = 200;
var
i: vesels skaitlis;
sākt
asyncHelper. MaxThreads: = 2 * sistēma. CPUC skaits;
ClearLog ('sākot');
priekš i: = 1 līdz nrItems darīt
sākt
asyncHelper. AddTask (TAsyncCalls. Izsaukt (AsyncMethod, i, Random (500)));
beigas;
Žurnāls ('viss iekšā');
// pagaidiet visu
//asyncHelper.WaitAll;
// vai atļauj atcelt visu neuzsākto, noklikšķinot uz pogas Atcelt visu:

kamēr NAV asyncHelper. Viss pabeigts darīt Pieteikums. Procesa ziņojumi;
Žurnāls ('pabeigts');
beigas;

Vai atcelt visu? - jāmaina vietne AsyncCalls.pas: (

Es arī vēlētos, lai būtu veids, kā "atcelt" tos uzdevumus, kuri atrodas baseinā, bet gaida to izpildi.

Diemžēl AsyncCalls.pas nenodrošina vienkāršu uzdevuma atcelšanas veidu, kad tas ir pievienots pavedienu fondam. IAsyncCall nav. Atcelt vai IAsyncCall. DontDoIfNotAlreadyExecuting vai IAsyncCall. NeverMindMe.

Lai tas darbotos, man nācās mainīt AsyncCalls.pas, mēģinot to mainīt pēc iespējas mazāk - tā ka tad, kad Andijs izlaiž jaunu versiju, man jāpievieno tikai dažas rindiņas, lai man būtu sava ideja “Atcelt uzdevumu” strādā.

Lūk, ko es izdarīju: IAsyncCall esmu pievienojis "procedūras atcelšana". Atcelšanas procedūra iestata lauku “FCancelled” (pievienots), kas tiek pārbaudīts, kad baseins gatavojas sākt uzdevuma izpildi. Man vajadzēja nedaudz mainīt IAsyncCall. Pabeigts (tā, ka zvana ziņojumi tiek pabeigti pat atcelta) un TAsyncCall. InternExecuteAsyncCall procedūra (neizpildīt zvanu, ja tas ir atcelts).

Tu vari izmantot WinMerge lai viegli atrastu atšķirības starp Andy oriģinālo asynccall.pas un manu mainīto versiju (iekļauta lejupielādē).

Jūs varat lejupielādēt pilnu avota kodu un izpētīt.

Grēksūdze

PAZIŅOJUMS! :)

 Atcelt aicinājumu metode aptur AsyncCall izsaukšanu. Ja AsyncCall jau ir apstrādāts, zvanam uz CancelInvocation nav nekādas ietekmes un atceltā funkcija atgriezīsies Nepatiesa, jo AsyncCall netika atcelts.
Atcelts metode atgriežas True, ja AsyncCall tika atcelts ar CancelInvocation.
Aizmirsti metode atslēdz IAsyncCall saskarni no iekšējās AsyncCall. Tas nozīmē, ka, ja vairs nebūs pēdējās atsauces uz IAsyncCall saskarni, asinhronais zvans joprojām tiks izpildīts. Saskarnes metodes radīs izņēmumu, ja pēc izsaukšanas Aizmirstiet, ja tas tiek izsaukts. Async funkcija nedrīkst ienākt galvenajā pavedienā, jo to varētu izpildīt pēc TThread. Sinhronizācijas / rindas mehānismu pārtrauca RTL, un tas var izraisīt strupceļa bloķēšanu.

Tomēr ņemiet vērā, ka joprojām varat gūt labumu no mana AsyncCallsHelper, ja jāgaida, kamēr visi async zvani tiks pabeigti ar “asyncHelper”. WaitAll "; vai ja jums ir nepieciešams "Atcelt visu".