Článok predložil Marcus Junglas
Pri programovaní obsluhy udalostí v Delphi (napr Po kliknutí v prípade TButton), prichádza čas, keď musí byť vaša aplikácia na chvíľu zaneprázdnená, napr. kód musí písať veľký súbor alebo komprimovať niektoré údaje.
Ak to urobíte, všimnete si to Zdá sa, že vaša aplikácia je uzamknutá. Váš formulár už nie je možné presunúť a tlačidlá nevykazujú známky života. Zdá sa, že havaroval.
Dôvod je ten, že aplikácia Delpi je jednovláknová. Kód, ktorý píšete, predstavuje iba veľa procedúr, ktoré sa volajú hlavným vláknom Delphi pri každej udalosti. Zvyšok času je hlavná téma spracovania správ systému a ďalších vecí, ako sú funkcie spracovania formulárov a komponentov.
Takže ak nedokončíte spracovanie svojich udalostí pomocou zdĺhavej práce, zabránite aplikácii spracovať tieto správy.
Bežným riešením tohto typu problémov je volanie „Aplikácia. ProcessMessages ". „Aplikácia“ je globálny objekt triedy TApplication.
Aplikácia. Procesy spracovávajú všetky čakajúce správy, ako sú pohyby okien, kliknutia na tlačidlá atď. Bežne sa používa ako jednoduché riešenie, aby vaša aplikácia zostala funkčná.
Bohužiaľ mechanizmus za „ProcessMessages“ má svoje vlastné vlastnosti, čo by mohlo spôsobiť veľké zmätenie!
Čo robí ProcessMessages?
PprocessMessages spracováva všetky čakajúce systémové správy vo fronte správ aplikácií. Systém Windows používa správy na „rozprávanie“ so všetkými bežiacimi aplikáciami. Interakcia používateľa sa do formulára prenáša prostredníctvom správ a spracováva ich „ProcessMessages“.
Ak napríklad myš klesá na TButton, ProgressMessages urobí všetko, čo by sa malo pri tejto udalosti stať, ako je napríklad prepíšte tlačidlo do „stlačeného“ stavu a, samozrejme, zavolajte na postup spracovania OnClick (), ak ste ho priradili.
To je problém: každé volanie na ProcessMessages môže obsahovať rekurzívne volanie akémukoľvek obsluhe udalosti. Tu je príklad:
Použite nasledujúci kód pre manipuláciu s tlačidlom OnClick pre párty (ďalej len „práca“). For-Statement simuluje dlhú úlohu spracovania s občasným volaním na ProcessMessages.
Zjednodušuje sa to kvôli lepšej čitateľnosti:
{in MyForm:}
WorkLevel: integer;
{OnCreate:}
WorkLevel: = 0;
procedúra TForm1.WorkBtnClick (Sender: TObject);
var
cyklus: celé číslo;
začať
inc (WorkLevel);
pre cyklus: = 1 na 5 robiť
začať
Memo1.Lines. Pridať ('- Work' + IntToStr (WorkLevel) + ', Cycle' + IntToStr (cycle);
Aplikácie. ProcessMessages;
spánok (1000); // alebo nejaká iná práca
koniec;
Memo1.Lines. Pridať ('Work' + IntToStr (WorkLevel) + 'skončil.');
dec (WorkLevel);
koniec;
BEZ "ProcessMessages" sa do poznámky zapíšu nasledujúce riadky, ak bolo tlačidlo stlačené dvakrát dvakrát:
- práca 1, cyklus 1
- práca 1, cyklus 2
- práca 1, cyklus 3
- práca 1, cyklus 4
- práca 1, cyklus 5
Práca 1 sa skončila.
- práca 1, cyklus 1
- práca 1, cyklus 2
- práca 1, cyklus 3
- práca 1, cyklus 4
- práca 1, cyklus 5
Práca 1 sa skončila.
Kým je postup zaneprázdnený, formulár nevykazuje žiadnu reakciu, ale druhé kliknutie vložilo do frontu správ systém Windows. Hneď po dokončení funkcie „OnClick“ sa znova zavolá.
Vrátane "ProcessMessages", výstup môže byť veľmi odlišný:
- práca 1, cyklus 1
- práca 1, cyklus 2
- práca 1, cyklus 3
- 2. práca, cyklus 1
- 2. práca, 2. cyklus
- práca 2, cyklus 3
- práca 2, cyklus 4
- 2. práca, cyklus 5
Práca 2 sa skončila.
- práca 1, cyklus 4
- práca 1, cyklus 5
Práca 1 sa skončila.
Zdá sa, že tentoraz formulár funguje znova a akceptuje akúkoľvek interakciu používateľa. Tlačidlo je teda stlačené na polceste počas vašej prvej „pracovnej“ funkcie AGAIN, ktorá bude okamžite spracovaná. Všetky prichádzajúce udalosti sa spracúvajú ako každé iné volanie funkcie.
Teoreticky sa počas každého hovoru na „ProgressMessages“ môže „na miesto“ vyskytnúť ŽIADNE množstvo kliknutí a správ používateľov.
S kódom buďte preto opatrní!
Iný príklad (v jednoduchom pseudokóde!):
procedúra OnClickFileWrite ();
var myfile: = TFileStream;
začať
myfile: = TFileStream.create ('myOutput.txt');
vyskúšať
zatiaľ čo BytesReady> 0 robiť
začať
myfile. Zápis (DataBlock);
dec (BytesReady, sizeof (DataBlock));
DataBlock [2]: = # 13; {testovací riadok 1}
Aplikácie. ProcessMessages;
DataBlock [2]: = # 13; {test line 2}
koniec;
konečne
myfile.free;
koniec;
koniec;
Táto funkcia zapisuje veľké množstvo údajov a pokúša sa „odomknúť“ aplikáciu pomocou „ProcessMessages“ zakaždým, keď sa zapíše blok údajov.
Ak používateľ znova klikne na tlačidlo, rovnaký kód bude vykonaný počas zapisovania súboru. Súbor teda nemožno otvoriť druhýkrát a postup zlyhá.
Možno vaša aplikácia urobí nejaké zotavenie po chybe, ako napríklad uvoľnenie vyrovnávacích pamätí.
Ako možný výsledok bude „Datablock“ uvoľnený a prvý kód „náhle“ vyvolá „porušenie prístupu“, keď k nemu pristupuje. V takom prípade: testovací riadok 1 bude fungovať, testovací riadok 2 zlyhá.
Lepší spôsob:
Aby ste to uľahčili, môžete nastaviť celý formulár „enabled: = false“, ktorý blokuje všetky vstupy používateľov, ale NEZARUČUJE používateľovi (všetky tlačidlá nie sú sivé).
Lepším spôsobom by bolo nastaviť všetky tlačidlá na „zakázané“, ale to by mohlo byť zložité, ak si napríklad chcete ponechať jedno tlačidlo „Zrušiť“. Musíte tiež prejsť všetkými komponentmi, aby ste ich zakázali, a keď sú znova aktivované, musíte skontrolovať, či by v zostávajúcom stave zostali nejaké zostávajúce.
Mohol by si zakázať podradené ovládacie prvky kontajnera, keď sa zmení vlastnosť Povolené.
Ako naznačuje názov triedy „TNotifyEvent“, mal by sa používať iba na krátkodobé reakcie na udalosť. Pre časovo náročný kód je najlepší spôsob, ako IMHO vložiť všetok „pomalý“ kód do vlastného vlákna.
Pokiaľ ide o problémy s správou "PrecessMessages" a / alebo povolením a zakázaním komponentov, použitie a druhé vlákno Zdá sa, že nie je vôbec príliš komplikovaný.
Pamätajte, že aj jednoduché a rýchle riadky kódu môžu visieť niekoľko sekúnd, napr. otvorenie súboru na diskovej jednotke bude možno musieť počkať, kým sa jednotka nevypne. Nevypadá veľmi dobre, ak sa zdá, že vaša aplikácia zlyhá, pretože jednotka je príliš pomalá.
To je všetko. Pri nasledujúcom pridaní aplikácie. ProcessMessages “, zamyslite sa dvakrát;)