Članak podnio Marcus Junglas
Kada programirate program za obradu događaja u Delphiju (kao što je Na klik događaj TButtona), dolazi vrijeme kada vaša aplikacija treba biti zauzeta neko vrijeme, npr. kôd treba napisati veliku datoteku ili komprimirati neke podatke.
Ako to učinite, primijetit ćete to čini se da je vaša aplikacija zaključana. Obrazac se više ne može premjestiti, a gumbi ne pokazuju znak života. Čini se da se srušio.
Razlog je to što je Delpi aplikacija jednostruka. Kôd koji pišete predstavlja samo gomilu postupaka koje Delphi zove glavna nit kad god se dogodi neki događaj. Ostalo je vrijeme glavna nit rukovanje sistemskim porukama i druge stvari kao što su funkcije upravljanja obrascima i komponentama.
Dakle, ako dovršenje posla ne dovršite dugotrajnim radom, spriječit ćete aplikaciju da obrađuje te poruke.
Uobičajeno rješenje za takvu vrstu problema je nazvati "Aplikacija. ProcessMessages”. "Aplikacija" je globalni objekt klase TApplication.
Aplikacija. Processmessages obrađuje sve poruke na čekanju kao što su pokreti prozora, klikovi na gumbe itd. Obično se koristi kao jednostavno rješenje kako bi aplikacija ostala u funkciji.
Nažalost, mehanizam koji stoji iza "ProcessMessages" ima svoje karakteristike, što može uzrokovati veliku zbrku!
Što su ProcessMessages?
PprocessMessages obrađuje sve sistemske poruke u čekanju poruka aplikacija. Windows koristi poruke za "razgovor" sa svim pokrenutim aplikacijama. Interakcija korisnika dovodi se u obrazac putem poruka i "ProcessMessages" s njima postupa.
Na primjer, ako miš pada na TButton, ProgressMessages čini sve što bi se trebalo dogoditi na ovom događaju, poput ponovno preslikavanje gumba u "pritisnuto" stanje i, naravno, poziv u postupak rukovanja OnClick () ako ste ga dodijelili.
U tome je problem: svaki poziv ProcessMessages može ponovo sadržavati rekurzivni poziv bilo kojem rukovatelju događaja. Evo primjera:
Sljedeći kôd upotrijebite za upravljač ravnim gumbom OnClick gumba ("rad"). Izjava za formulu simulira dugačak proces obrade s nekim pozivima u ProcessMessages.
Ovo je pojednostavljeno radi bolje čitljivosti:
{na MyFormu:}
WorkLevel: cijeli broj;
{OnCreate:}
WorkLevel: = 0;
postupak TForm1.WorkBtnClick (Pošiljatelj: TObject);
var
ciklus: cijeli broj;
početi
inc (WorkLevel);
za ciklus: = 1 do 5 čini
početi
Memo1.Lines. Add ('- Work' + IntToStr (WorkLevel) + ', Cycle' + IntToStr (ciklus);
Primjena. ProcessMessages;
spavati (1000); // ili neko drugo djelo
kraj;
Memo1.Lines. Dodaj ('Rad' + IntToStr (WorkLevel) + 'završio.');
dec (WorkLevel);
kraj;
BEZ "ProcessMessages" u memoriju se upisuju sljedeći redovi, ako je gumb kratko pritisnut Dvaput:
- Rad 1, ciklus 1
- Rad 1, ciklus 2
- Rad 1, ciklus 3
- Rad 1, ciklus 4
- Rad 1, ciklus 5
Rad 1 je završio.
- Rad 1, ciklus 1
- Rad 1, ciklus 2
- Rad 1, ciklus 3
- Rad 1, ciklus 4
- Rad 1, ciklus 5
Rad 1 je završio.
Dok je postupak zauzet, obrazac ne pokazuje nikakvu reakciju, ali je drugi klik Windows stavio u red poruka. Odmah nakon što je "OnClick" završen, bit će ponovno pozvan.
Uključujući "ProcessMessages", izlaz može biti vrlo različit:
- Rad 1, ciklus 1
- Rad 1, ciklus 2
- Rad 1, ciklus 3
- 2. rad, ciklus 1
- Rad 2, ciklus 2
- Rad 2, ciklus 3
- Rad 2, ciklus 4
- Rad 2, ciklus 5
Rad 2 je završio.
- Rad 1, ciklus 4
- Rad 1, ciklus 5
Rad 1 je završio.
Ovaj put čini se da obrazac ponovno radi i prihvaća bilo kakvu interakciju s korisnicima. Dakle, tipka se pritisne na pola puta tijekom prve funkcije "radnik" PROTIV, kojom ćete rukovati odmah. Svi dolazni događaji obrađuju se kao i bilo koji drugi pozivi funkcija.
Teoretski, tijekom svakog poziva na "ProgressMessages" može se dogoditi bilo koji broj klikova i korisničkih poruka "na mjestu".
Zato budite oprezni sa svojim kodom!
Drugačiji primjer (u jednostavnom pseudo-kodu!):
postupak OnClickFileWrite ();
var myfile: = TFileStream;
početi
myfile: = TFileStream.create ('myOutput.txt');
probati
dok BytesReady> 0 čini
početi
mojaDatoteka. Pišite (DataBlock);
dec (BytesReady, sizeof (DataBlock));
DataBlock [2]: = # 13; {ispitna linija 1}
Primjena. ProcessMessages;
DataBlock [2]: = # 13; {ispitna linija 2}
kraj;
konačno
myfile.free;
kraj;
kraj;
Ova funkcija piše veliku količinu podataka i pokušava "otključati" aplikaciju koristeći "ProcessMessages" svaki put kada se upiše blok podataka.
Ako korisnik ponovo pritisne gumb, izvršit će se isti kod dok se datoteka još uvijek piše. Dakle, datoteku nije moguće otvoriti drugi put i postupak ne uspije.
Možda će vaša aplikacija izvršiti neki oporavak pogrešaka poput oslobađanja međuspremnika.
Kao mogući rezultat bit će oslobođen "Datablock" i prvi će kôd "iznenada" pokrenuti "Kršenje pristupa" kada mu pristupi. U ovom slučaju: ispitna linija 1 će raditi, ispitna linija 2 će se srušiti.
Bolji način:
Da biste ga olakšali, mogli biste postaviti cijeli obrazac "omogućen: = netočno", koji blokira sve korisničke unose, ali NIJE to pokazao korisniku (svi gumbi nisu sive).
Bolji način bi bio postaviti sve gumbe na "onemogućeno", ali to može biti složeno ako želite primjerice zadržati jedan gumb "Otkaži". Također trebate proći sve komponente da biste ih onemogućili i kada su ponovo omogućene, morate provjeriti treba li ostati još nekih u stanju onesposobljenosti.
Mogao bi onemogućite kontrole nadređenih spremnika kada se promijeni svojstvo Omogućeno.
Kao što ime klase "TNotifyEvent" sugerira, trebalo bi ga koristiti samo za kratkotrajne reakcije na događaj. Najbolji put za kôd je IMHO da se sav "spor" kod stavi u vlastiti Thread.
Što se tiče problema s "PrecessMessages" i / ili omogućavanjem i isključivanjem komponenata, upotreba a druga nit čini se da uopće nije previše komplicirano.
Zapamtite da čak i jednostavne i brze linije koda mogu visiti nekoliko sekundi, npr. otvaranje datoteke na diskovnom pogonu možda će morati pričekati dok se pogon ne završi. Ne izgleda baš dobro ako se čini da se vaša aplikacija ruši jer je pogon prespor.
To je to. Sljedeći put kad dodate "Aplikacija. ProcessMessages ", razmisli dvaput;)