Multithreaded Application Tutorial/sk

From Free Pascal wiki
Jump to navigationJump to search

Deutsch (de) English (en) español (es) français (fr) 日本語 (ja) polski (pl) português (pt) русский (ru) slovenčina (sk) 中文(中国大陆)‎ (zh_CN)

Prehľad

Táto stránka vám pomôže s pochopením vývoja a debugovania viacvláknových programov s použitím Free Pascale a Lazarusa.

Viacvláknový program je program, ktorý vytvorí dve a viacero vlákien pre vykonanie svojej práce v rovnaký čas.

Jedno z vlákien je hlavné. Hlavné vlákno je vlákno vytvorené operačným systémom pri štarte programu. Hlavné vlákno musí byt jediné, ktoŕe obnovuje komponenty, ktoré sú spojené s uživateľom (v opačnom prípade môže program uschnúť).

Hlavná myšlienka viacvláknového programovania spočíva v tom, že zatiaľ čo sa bude vykonávať vedľajšia činnosť ktorá by blokovoala, tak hlavné prostredie pre prácu s uživateľom môže fungovať ďalej ako hlavné vlákno.

Ďalšie použitie vlákien je ak chceme mať viacej raktívnu aplikáciu. Ak napr. vytvoríte aplikáciu v ktorej keď sa stlačí gombík tak sa spracuje veľa dát (spracovanie trvá..) tak by sa v normálnom prípade mohlo zdať že sa program "zasekol", čo nieje zrovna chcená vlastnosť. Ak by spracovanie bežalo vo vedlajšom vlákne, aplikácia zostáva reaktívnou ako keby sa nič nedialo. V takýchto prípadoch je dobrý zvyk povoliť štart len jedného vedlajšieho vlákna. (V opačnom prípade by mohli nastať nežiadúce situácie keď by užívateľ naštartoval viacej vlákien, ktoŕe by robili to isté)

Ďalšie možné využitie je pri sieťovom servery, kde sa musí obsluhovať naraz viac spojení.

Trieda TThread

Najjednoduchší spôsob na vytvorenie viacvláknovej aplikácie je trieda TThread.

Táto trieda umožnuje vytvorenie dodatočného vlákna (popri hlavnom) jednoduchým spôsobom.

V štandardných prípadoch musíte overridnúť 2 metódy: konštruktor Create a procedúru Execute.

V konštruktore pripravíte vlákno na chod. Nastavíte základné hodnoty premenným alebo vlastnostiam podľa potreby. Nadriadený konštruktor TThreadu vyžaduje parameter nazívaný Suspended. Ako ste možno uhádli, byť Suspended = True zabráni vláknu aby sa spustilo automaticky po vytvorení. Ak Suspended = False, vlákno sa spustí hned po vytvorení. Ak ste vlákno vytvorili suspended, musíte ho "spustit" pomocou Resume.

Od verzie FPC 2.0.1 a vyžšej má TThread.Create aj implicitný parameter Stack Size. Od teraz môžete meniť veľkosť stacku pre jednotlivé vlákna. Dobrým príkladom využitia tohto parametra je, ak vo vlákne robíte hlboké rekurzie. Ak nešpecifikujete parameter Stack Size, tak je vybraná štandardná veľkosť v závislosti od operačného systému.

V overridnutej metóde Execute napíšete kôd ktorý sa má počas behu vlákna vykonať.

Trieda TThread ma jednu veľmi dôležitú vlastnost: Terminated : boolean;

Ak má vlákno cyklus (čo je obvyklé), musí sa v cykle kontrolovať, či je Terminated true (štandardne je false). Takže je nutné sa v každom prechode v cykle pozrieť, či je Terminated True, a ak áno, musí sa metóda Execute ukončiť tak rýchlo ako je to možne. (netreba zabúdať na uvolnenie pamäťe ak bola nejaka naalokovaná)

Zapamätajte si že vlasnosť Terminate nerobí sama o sebe nič. Je nutné naprogramovať metódu Execute tak, aby ju brala do úvahy.

Ako bolo vysvetlené, nieje možné aby sa z vedlajšieho vlákna nejakým spôsobom ovlivnovalo grafické prostredie a komponenty. Aby sme niečo uživateľovi ukázali, musíme to spraviť cez hlavné vlákno. Z tohto dôvodu existuje metóda Synchronize. Synchronize vyžaduje metódu (bez argumentov) ako argument. Keď svoju metódu zavoláte cez Synchronize(@MojaMetoda), vlákno bude "zapauznuté" a kód v MojaMetoda sa vykoná v hlavnom vlákne, po čom sa následne uvoľní vedlajšie vlákno.

Ďalšou dôležitou vlastnosťou je FreeOnTerminate. Ak je táto vlastnosť true, tak je objekt TThreadu automaticky uvoľnený z pamäti po dokončení vykonávania (metódy .Execute). V opačnom prípade musí aplikácia uvoľniť objekt sama.

Príklad:

 Type
   TMyThread = class(TThread)
   private
     fStatusText : string;
     procedure ShowStatus;
   public
     Constructor Create(Suspended : boolean); override;
     procedure Execute; override;
   end;
 constructor TMyThread.Create(Suspended : boolean);
 begin
   inherited Create(Suspended);
   FreeOnTerminate := True;
 end;
 procedure TMyThread.ShowStatus;
 begin
   Form1.Caption := fStatusText;
 end;

 procedure TMyThread.Execute;
 var
   newStatus : string;
 begin
   fStatusText := 'Starting...';
   Synchronize(Showstatus);
   fStatusText := 'Running...';
   while (not Terminated) and ([vaša podmienka]) do
     begin
       ...
       [sem vložte kód hlavného cyklu]
       ...
       if NewStatus <> fStatus then
         begin
           fStatusText = newStatus;
           Synchronize(Showstatus);
         end;
     end;
 end;

On the application,

 var
   MyThread : TMyThread;
 begin
   MyThread := TMyThread.Create(True); // Takto sa vlákno vytvorí "suspended"
   ...
   [sem vložte inicializačný kód]
   ...
   MyThread.Resume;
 end;

Dôležité veci na ktoré je treba dávať pozor

Ak vo Windowse používate vlákna a máte zapnutú kontrolu pretečenia stacku (-Ct), vlákno sa nevykoná. Z dôvodu implementácie kontroly stacku sa po TThread.Create vlákno nevykoná ale vyvolá vínimku. Táto vínimka NIEJE vyvolaná v hlavnom vlákne, čo znamená že nieje zaznamenaná. Správny spôsob vytvárania vlákien aby ste výnimky zaznamenali je:


    MyThread:=TThread.Create(False);
    if Assigned(MyThread.FatalException) then
      raise MyThread.FatalException;


Tento kód zaruči, že ak sa pri vytváraní vlákna vyvolá vínimka, bude vyvolaná aj v hlavnom vlákne. Jeviný spôsob ako sa vo Windowse vyhnút tejto chybe je nepoužívať kontrolu stacku (prepínač -Ct). Kontrola stacku je štandardne vypnutá.

Unity podrebné na viacvláknové programovanie

Vo windowse nieje treba žiadnych špeciálnych unít. V linuxe je treba použiť unitu cthreads, ktorá musí byť prvá v hlavnom programe!

Váš program bude vizerať takto:

 program MyMultiThreadedProgram;
 {$H+}
 uses
 {$ifdef linux}
   cthreads,
 {$endif}
   Interfaces, // this includes the LCL widgetset
   Forms
   { add your units here },

Podpora SMP


Dobrá správa je, že pokiaľ vaša aplikácia beži na viacerých vláknach má automaticky podporu SMP!

Debugovanie viacvláknových programov v Lazaruse

Debugovanie v lazaruse ešte nieje úplne funkčné. Ale ak sa pokúsite debugovat viacvláknovú aplikáciu v Linuxe uschne vám X server.

Nieje známe riešenie ale jeden dočasný spôsob je:

Vytvorenie novej inštancie X pomocou:

 X :1 &

Naštartuje sa nový X a keď prepnete do ďalšej plochy (stlačením CTRL+ALT+F7) budete môcť ísť späť do novej grafickej plochy stlačením CTRL+ALT+F8 (ak skratky nefungujú skúste CTRL+ALT+F2... to fungovalo na Slackware).

Potom môžete ak chcete vytvoriť nové sedenie na X:

 gnome-session --display=:1 &

Potom v Lazaruse v nastavení projektu / run parameters zašktnite "Use display" a vložte :1.

Teraz sa aplikácia spustí na X serveri č. 2 a vy ju môžete debugovať zo servera 1.

Toto bolo otestované s Free Pascal 2.0 a Lazarus 0.9.10 na Windowse a Linuxe