25. Pou₧φvßnφ vlßken I
  1. C++ Builder poskytuje n∞kolik objekt∙, kterΘ usnad≥ujφ zßpis vφcevlßknov²ch aplikacφ. VφcevlßknovΘ aplikace jsou aplikace, kterΘ obsahujφ n∞kolik soub∞₧n²ch cest provßd∞nφ. P°i pou₧itφ jednoho vlßkna, program musφ zastavit vÜechno provßd∞nφ, kdy₧ Φekß na dokonΦenφ pomalΘho procesu (nap°. zp°φstupn∞nφ souboru na disku, komunikaci s dalÜφmi poΦφtaΦi nebo zobrazovßnφ multimΘdiφ). CPU Φekß, dokud proces nenφ dokonΦen. S vφce vlßkny, naÜe aplikace m∙₧e pokraΦovat v provßd∞nφ dalÜφch vlßknen, kdy₧ jedno vlßkno Φekß na v²sledek pomalΘho procesu.

  2. Chovßnφ programu m∙₧e b²t Φasto organizovßno do n∞kolika paralelnφch proces∙, kterΘ pracujφ nezßvisle. Ka₧d² z t∞chto paralelnφch proces∙ m∙₧e b²t provßd∞n soub∞₧n∞ pomocφ jednoho vlßkna. Jednotliv²m vlßkn∙m lze p°i°adit prioritu, Φφm urΦφme jak mnoho Φasu CPU bude vlßkno pou₧φvat. Pokud systΘm, na kterΘm b∞₧φ nßÜ program, mß n∞kolik procesor∙, m∙₧eme zv²Üit v²konnost rozd∞lenφm prßce do n∞kolika vlßken a spouÜt∞t je souΦasn∞ na r∙zn²ch procesorech.
    OperaΦnφ systΘm Windows NT podporuje pravΘ multiprocesorovΘ zpracovßnφ, pokud to umo₧≥uje pou₧it² hardware. Windows 98 toto pouze simuluje.
  3. K reprezentaci provßd∞nΘho vlßkna v naÜφ aplikaci v∞tÜinou pou₧ijeme objekt vlßkna. Objekty vlßkna zjednoduÜujφ zßpis vφcevlßknov²ch aplikacφ zaobalenφm nejΦast∞jÜφch po₧adavk∙ na vlßkna. Objekty vlßken neumo₧≥ujφ ovlßdat bezpeΦnostnφ atributy nebo velikost zßsobnφku naÜeho vlßkna. Abychom to mohli provΘst, musφme pou₧φt funkci API Windows CreateThread.

  4. K pou₧itφ objektu vlßkna v naÜi aplikaci, musφme vytvo°it potomka TThread. Pro vytvo°enφ potomka TThread zvolφme File | New a na strßnce New vybereme Thread Object. Dßle jsme dotßzßni na jmΘno t°φdy (nenφ zde automaticky p°idßvßno T p°ed jmΘno) pro nßÜ nov² objekt vlßkna. C++ Builder vytvo°φ zdrojov² a hlaviΦkov² soubor k implementaci vlßkna. Automaticky vygenerovan² zdrojov² soubor obsahuje kostru k≤du pro nßÜ nov² objekt vlßkna. Pokud jej nazveme TMojeVlakno, pak soubor mß obsah:
    #include <vcl.h>
    #pragma hdrstop
    #include "Unit1.h"
    #pragma package(smart_init)
    __fastcall TMojeVlakno::TMojeVlakno(bool CreateSuspended)
        : TThread(CreateSuspended)
    {
    }
    //---------------------------------------------------------------
    void __fastcall TMojeVlakno::Execute()
    {
        //---- Zde umφstφme k≤d vlßkna ----
    }
    Musφme doplnit k≤d konstruktoru a k≤d metody Execute.
    Konstruktor pou₧ijeme k inicializaci naÜφ novΘ t°φdy vlßkna. M∙₧eme zde p°i°adit implicitnφ prioritu vlßkna a urΦit, zda vlßkno bude automaticky uvoln∞no po dokonΦenφ provßd∞nφ.
    Priorita urΦuje, kolik prost°edk∙ vlßkno zφskß, kdy₧ operaΦnφ systΘm plßnuje vyu₧itφ Φasu CPU pro vÜechna vlßkna v naÜi aplikacφ. VyÜÜφ prioritu pou₧ijeme pro vlßkna zpracovßvajφcφ kritickΘ ·lohy a ni₧Üφ prioritu pro vlßkna provßd∞jφcφ ostatnφ ·koly. Pro urΦenφ priority naÜeho vlßkna nastavφme vlastnost Priority. Je zde sedm mo₧n²ch hodnot (viz nßsledujφcφ tabulka):
     
    Hodnota Priorita
    tpIdle Vlßkno je provßd∞no pouze, kdy₧ systΘm je neΦinn². Windows nep°eruÜuje provßd∞nφ ostatnφch vlßken k provedenφ t∞chto vlßken.
    tpLowest Priorita vlßkna je dva body pod normßlem.
    tpLower Priorita vlßkna je jeden bod pod normßlem.
    tpNormal Vlßkno mß normßlnφ prioritu.
    tpHigher Priorita vlßkna je jeden bod nad normßlem.
    tpHighest Priorita vlßkna je dva body nad normßlem.
    tpTimeCritical Vlßkno zφskß nejvyÜÜφ prioritu.

    Nßsledujφcφ k≤d ukazuje konstruktor vlßkna nejni₧Üφ priority, kterΘ je provßd∞no na pozadφ (nep°eruÜuje ostatnφ aplikace):
    __fastcall TMyThread::TMyThread(bool CreateSuspended):
                          TThread(CreateSuspended)
    {
      Priority = tpIdle;
    }
    ╚asto aplikace provßdφ jistΘ vlßkno pouze jednou. V tomto p°φpad∞ je nejvhodn∞jÜφ, kdy₧ objekt vlßkna uvolnφ sßm sebe. To nastane, kdy₧ vlastnost FreeOnTerminate je nastavena na true. Jestli₧e objekt vlßkna reprezentuje ·lohy aplikace, kterΘ jsou provßd∞ny n∞kolikrßt (nap°. v reakci na akci u₧ivatele nebo p°φchodu externφ zprßvy), pak m∙₧eme zv²Üit v²konnost odlo₧enφm vlßkna pro op∞tovnΘ pou₧itφ (namφsto jeho zruÜenφ a op∞tovnΘho vytvo°enφ). To provedeme nastavenφ vlastnosti FreeOnTerminate na false.
    Metoda Execute je Φinnost naÜeho vlßkna. M∙₧eme zde urΦit, jak vlßkno bude chßpßno naÜφ aplikacφ, mimo sdφlenφ stejnΘho procesovΘho prostoru. Zßpis vlßkna je nepatrn∞ slo₧it∞jÜφ ne₧ zßpis samostatnΘho programu, nebo¥ se musφme ujistit, ₧e nep°episujeme pam∞¥ pou₧φvanou jin²mi vlßkny v aplikaci. Na druhΘ stran∞, proto₧e vlßkno sdφlφ stejn² procesov² prostor s ostatnφmi vlßkny, m∙₧eme pou₧φt sdφlenou pam∞¥ pro komunikaci mezi vlßkny.
    Kdy₧ pou₧φvßme objekty z hierarchie objekt∙ C++ Builderu, pak jejich vlastnosti a metody nezajiÜ¥ujφ bezpeΦnΘ vlßkno. Tj. zp°φstup≥ovßnφm vlastnostφ nebo provßd∞nφm metod mohou b²t provßd∞ny n∞kterΘ akce, pou₧φvajφcφ pam∞¥, kterß nenφ chrßn∞na p°ed akcemi z ostatnφch vlßken. Hlavnφ vlßkno VCL je nastaveno mimo p°φstup k objekt∙m VCL. Jednß se o vlßkno, kterΘ zpracovßvß zprßvy Windows p°ijatΘ komponentami v naÜφ aplikaci.
    Jestli₧e vÜechny objekty zp°φstup≥ujφ svΘ vlastnosti a provßd∞jφ svΘ metody v jednom vlßkn∞, pak nemohou nastat problΘmy s interakcφ naÜich objekt∙ s jin²mi. K pou₧itφ hlavnφho vlßkna vytvo°φme samostatnou funkci, kterß provßdφ po₧adovanΘ akce. Tuto samostatnou funkci volßme z metody Synchronize vlßkna. Nap°.
    void __fastcall TMyThread::PushTheButton(void)
    {
      Button1->Click();
    }
    void __fastcall TMyThread::Execute()
    {
      ...
      Synchronize((TThreadMethod)PushTheButton);
      ...
    }
    Synchronize Φekß pro hlavnφ vlßkno VCL na vstup cyklu zprßv, a potom provede p°edanou metodu.
    Ne v₧dy je nutno pou₧φvat hlavnφ vlßkno VCL. N∞kterΘ objekty jsou vlßknov∞ bezpeΦnΘ. Kdy₧ vφme, ₧e objekt je vlßknov∞ bezpeΦn², pak nenφ nutnΘ pou₧φt metodu Synchronize, co₧ zvyÜuje v²konnost, nebo¥ nenφ nutno Φekat na vstup vlßkna do cyklu zprßv. Metodu Synchronize nenφ nutno pou₧φt v nßsledujφcφch situacφch:

    NaÜe metoda Execute a funkce jφ volanΘ, majφ svΘ vlastnφ lokßlnφ prom∞nnΘ, stejn∞ jako libovolnΘ jinΘ funkce C++. Tyto funkce mohou takΘ p°istupovat ke globßlnφm prom∞nn²m. Globßlnφ prom∞nnΘ poskytujφ mechanismus pro komunikaci mezi vlßkny. N∞kdy, ale pot°ebujeme pou₧φt prom∞nnΘ, kterΘ jsou globßlnφ pro vÜechny funkce b∞₧φcφ ve vlßknu, ale nejsou sdφleny dalÜφmi instancemi stejnΘ t°φdy vlßkna. Pot°ebujeme tedy deklarovat vlßknovΘ lokßlnφ prom∞nnΘ. Provedeme to p°idßnφm modifikßtoru __thread k deklaraci prom∞nnΘ. Nap°.
    int __thread x;
    deklaruje prom∞nnou typu int, kterß je soukromß pro ka₧dΘ vlßkno v aplikaci, ale je globßlnφ v ka₧dΘm vlßknu. Modifikßtor __thread m∙₧e b²t pou₧it pouze s globßlnφmi a statick²mi prom∞nn²mi. Nem∙₧e b²t pou₧it s ukazateli nebo funkcemi. Programov² prvek, kter² vy₧aduje b∞hovou inicializaci nebo finalizaci nem∙₧e b²t deklarovßn s __thread. Nßsledujφcφ deklarace vy₧aduje b∞hovou inicializaci a je tedy nedovolenß:
    int f();
    int __thread x = f();    // nedovoleno
    Vytvo°enφ instance t°φdy s u₧ivatelem definovan²m konstruktorem nebo destruktorem vy₧aduje b∞hovou inicializaci a je tedy nedovolen²:
    class X  {

       X( );
       ~X( );
    };
    X __thread myclass;   // nedovoleno
    NaÜe vlßkno zaΦφnß b∞₧et p°i volßnφ metody Execute a pokraΦuje, dokud Execute neskonΦφ. Pou₧φvß se model, ve kterΘm vlßkno provßdφ specifickΘ ·koly a kdy₧ je provede, pak skonΦφ. N∞kdy ale aplikace pot°ebuje provßd∞t vlßkno dokud nenφ spln∞na n∞jakß externφ podmφnka.
    M∙₧eme umo₧nit ostatnφm vlßkn∙m signalizovat, ₧e vlßkno ukonΦilo provßd∞nφ a to testovßnφm vlastnosti Terminated. Kdy₧ jinΘ vlßkno vy₧aduje ukonΦenφ naÜeho vlßkna, pak volß metodu Terminate. Terminate nastavuje vlastnost Terminated naÜeho vlßkna na true. NaÜe metoda Execute m∙₧e vlastnost Terminated pou₧φt nap°. takto:
    void __fastcall TMyThread::Execute()
    {
      while (!Terminated)
        ProvedeniAkce();
    }
    M∙₧eme centralizovat vyΦiÜ¥ujφcφ k≤d, kter² je proveden, kdy₧ naÜe vlßkno ukonΦφ provßd∞nφ. P°ed dokonΦenφm prßce vlßkna vznikß udßlost OnTerminate. VyΦiÜ¥ujφcφ k≤d umis¥ujeme do obsluhy udßlosti OnTerminate a tak zajistφme, ₧e bude v₧dy proveden. Obsluha udßlosti OnTerminate ji₧ nenφ spuÜt∞na jako Φßst naÜeho vlßkna. Je spuÜt∞na v kontextu hlavnφho vlßkna VCL naÜφ aplikace. V obsluze OnTerminate tedy nenφ mo₧no pou₧φvat lokßlnφ prom∞nnΘ vlßkna a m∙₧eme bezpeΦn∞ p°istupovat ke vÜem komponentßm a objekt∙m VCL.

  5. Kdy₧ zapisujeme k≤d, kter² b∞₧φ p°i provßd∞nφ naÜeho vlßkna, musφme p°edpoklßdat chovßnφ dalÜφch vlßken, kterΘ mohou b²t spuÜt∞ny souΦasn∞. V jist²ch p°φpadech musφme zabrßnit dv∞ma vlßkn∙m v pou₧itφ stejnΘho globßlnφho objektu nebo prom∞nnΘ ve stejnΘ dob∞. K≤d v jednom vlßkn∞ m∙₧e zßviset na v²sledcφch ·loh provßd∞n²ch dalÜφmi vlßkny.

  6. Pro zabrßn∞nφ ostatnφm vlßkn∙m v p°φstupu ke globßlnφm objekt∙m nebo prom∞nn²m m∙₧eme blokovat provßd∞nφ ostatnφch vlßken, dokud naÜe vlßkno nedokonΦφ operaci. Lze pou₧φt uzamykßnφ objekt∙ nebo kritickou sekci.
    N∞kterΘ objekty majφ zabudovan² zßmek k zabrßn∞nφ pou₧φvßnφ dalÜφmi vlßkny. Nap°. objekty plßtna (TCanvas a jeho potomci) majφ metodu Lock, kterß zabra≥uje dalÜφm vlßkn∙m v p°φstupu k plßtnu, dokud nenφ volßna metoda Unlock. Objektovß hierarchie takΘ obsahuje vlßknov∞ bezpeΦn² seznam objekt∙, TThreadList. Volßnφ TThreadList::LockList vracφ seznam objekt∙, kter² je takΘ blokovßn p°ed dalÜφmi vlßkny dokud nenφ volßna metoda UnlockList. Volßnφ TCanvas::Lock nebo TThreadList::LockList m∙₧e b²t bezpeΦn∞ vno°ovßno. UzamΦenφ nenφ uvoln∞no, dokud nenφ odemΦeno poslednφ uzamΦenφ na souΦasnΘm vlßknu.
    Pokud objekt nemß zabudovan² zßmek, pak m∙₧eme pou₧φt kritickou sekci. Kritickß sekce pracuje jako brßna, kterß umo₧≥uje v jednom Φase vstup pouze jednoho vlßkna. Pro pou₧itφ kritickΘ sekce vytvo°φme globßlnφ instanci TCriticalSection. TCriticalSection mß dv∞ metody: Acquire (kterß zabra≥uje dalÜφm vlßkn∙m v provßd∞nφ sekce) a Release (odstra≥uje blokovßnφ).
    Ka₧dß kritickß sekce je p°i°azena ke globßlnφ pam∞ti, kterou mß chrßnit. Ka₧dΘ vlßkno, pou₧φvajφcφ tuto globßlnφ pam∞¥, musφ nejprve pou₧φt metodu Acquire k zajiÜt∞nφ toho, aby dalÜφ vlßkna ji nemohla pou₧φt. Po ukonΦenφ, vlßkno volß metodu Release a dalÜφ vlßkna mohou p°istoupit ke globßlnφ pam∞ti volßnφm Acquire. Vlßkna, kterß ignorujφ kritickou sekci a p°istupujφ ke globßlnφ pam∞ti bez volßnφ Acquire, mohou zp∙sobit problΘmy soub∞₧nΘho p°φstupu. Nap°. p°edpoklßdejme aplikaci, kterß mß kritickou sekci pLockXY blokujφcφ p°φstup ke globßlnφm prom∞nn²m X a Y. Ka₧dΘ vlßkno, kterΘ pou₧φvß X nebo Y musφ pou₧φt kritickou sekci takto:
    pLockXY->Acquire(); // uzamknutφ pro ostatnφ vlßkna
    Y = sin(X);
    pLockXY->Release();
    Kdy₧ pou₧φvßme objekty z hierarchie objekt∙ VCL, pou₧ijeme hlavnφ vlßkno VCL k provedenφ naÜeho k≤du. Pou₧itφ hlavnφho vlßkna VCL zajiÜ¥uje, ₧e objekty p°istupujφ k pam∞ti, kterß m∙₧e b²t pou₧ita objekty VCL z jin²ch vlßken, nep°φmo. S hlavnφm vlßknem VCL jsme se ji₧ seznßmili. Tento problΘm je mo₧no °eÜit i pomocφ lokßlnφch prom∞nn²ch vlßkna o  kter²ch jsme ji₧ takΘ mluvili.
  7. Pokud naÜe vlßkno musφ Φekat na dalÜφ vlßkna k dokonΦenφ n∞jakΘ ·lohy, pak m∙₧eme °φci, ₧e u naÜeho vlßkna je doΦasn∞ potlaΦeno provßd∞nφ. M∙₧eme Φekat na kompletnφ dokonΦenφ jinΘho vlßkna nebo Φekat na signßl od jinΘho vlßkna informujφcφ, ₧e byla dokonΦena n∞jakß ·loha.

  8. Pro Φekßnφ na dokonΦenφ provßd∞nφ jinΘho vlßkna pou₧φvßme metodu WaitFor tohoto jinΘho vlßkna. WaitFor nekonΦφ dokud toto jinΘ vlßkno nenφ dokonΦeno a to dokonΦenφm jeho metody Execute nebo vznikem v²jimky. Nap°. nßsledujφcφ k≤d Φekß, dokud jinΘ vlßkno nezaplnφ seznam objekt∙ vlßkna p°ed zp°φstupn∞nφm seznamu naÜemu vlßknu:
    if (pListFillingThread->WaitFor())
    {
      for (TList *pList = ThreadList1->LockList(), int i = 0;
           i < pList->Count; i++)
        ProcessItem(pList->Items[i]);
      ThreadList1->UnlockList();
    }
    Nevolejte metodu WaitFor vlßkna, kterΘ pou₧φvß Synchronize pro hlavnφ vlßkno VCL. Pokud hlavnφ vlßkno VCL mß volßnφ WaitFor, pak ostatnφ vlßkna nezφskajφ vstup cyklu zprßv a Synchronize nikdy neskonΦφ. Objekty vlßken detekujφ tento stav a generujφ v²jimku EThread ve vlßkn∞, kterΘ volß Synchronize.
    V p°edchozφm p°φklad∞, je seznam prvk∙ zp°φstupn∞n pouze tehdy, kdy₧ metoda WaitFor indikuje, ₧e seznam byl ·sp∞Ün∞ zapln∞n. Tato vrßcenß hodnota musφ b²t p°i°azena metodou Execute vlßkna na kterΘ Φekßme. Proto₧e vlßkna kterß volajφ WaitFor pot°ebujφ znßt v²sledek provßd∞nΘho vlßkna a ne k≤d, metody Execute nevracejφ ₧ßdnou hodnotu. Mφsto toho Execute nastavuje vlastnost ReturnValue. ReturnValue je pak vrßcena metodou WaitFor kdy₧ je volßna jin²m vlßknem. VrßcenΘ hodnoty jsou celß Φφsla. Jejich v²znam je urΦen naÜφ aplikacφ.
    M∙₧eme takΘ Φekat na dokonΦenφ n∞jakΘ operace jinΘho vlßkna a ne na kompletnφ dokonΦenφ provßd∞nφ vlßkna. K tomuto ·Φelu pou₧ijeme objekt udßlosti. Objekt udßlosti (TEvent) musφ b²t vytvo°en v globßlnφm rozsahu (musφ b²t viditeln² ve vÜech vlßknech). Kdy₧ vlßkno dokonΦφ operaci, na kterou Φekajφ jinß vlßkna, je volßno TEvent::SetEvent. SetEvent spustφ signßl a jinß vlßkna mohou testovat zda operace byla dokonΦena. K vypnutφ signßlu pou₧ijeme metodu ResetEvent.
    Nap°. nßsledujφcφ k≤d zapisuje obsah seznamu °et∞zc∙ do souboru a signalizuje dokonΦenφ zßpisu do souboru. Vypnutφm signßlu p°ed zßpisem souboru dosßhneme toho, ₧e ostatnφ vlßkna nemohou k souboru p°istupovat b∞hem zßpisu.
    void __fastcall WriteTheStrings(void)
    {
      StringList1->SaveToFile("Example.txt");
    }
    void __fastcall TWritingThread::Execute()
    {
      ...
      Event1->ResetEvent(); // vypnutφ signßlu
      Synchronize((TThreadMethod)WriteTheStrings);
      Event1->SetEvent();
     ...
    }
    Ostatnφ vlßkna testujφ signßl volßnφm metody WaitFor. WaitFor Φekß specifikovan² Φasov² interval na nastavenφ signßlu a vracφ jednu hodnotu z nßsledujφcφ tabulky:
     
    Hodnota V²znam
    wrSignaled Signßl udßlosti byl nastaven
    wrTimeout Specifikovan² Φas vyprÜel p°ed nastavenφm signßlu.
    wrAbandored Objekt udßlosti byl zruÜen p°ed vyprÜenφm ΦasovΘho intervalu.
    wrError V²skyt chyby b∞hem Φekßnφ.

    Nßsledujφcφ k≤d testuje zda soubor zapisovan² v p°edchozφm p°φklad∞ je mo₧no bezpeΦn∞ Φφst. ╚asov² interval je nastaven na 500 milisekund:
    if (Event1->WaitFor(500) == wrSignaled)
      // Φtenφ °et∞zc∙
    Jestli₧e nechceme zastavit Φekßnφ na udßlost vyprÜenφm Φasu, p°edßme metod∞ WaitFor parametr INFINITE. P°i pou₧itφ INFINITE musφme b²t opatrnφ, proto₧e naÜe vlßkno bude stßle Φekat na nastavenφ signßlu.
    SpouÜt∞nφm objekt∙ vlßken se budeme zab²vat v nßsledujφcφ kapitole.


  9. Nynφ zaΦneme vytvß°et vφcevlßknovou aplikaci. P°edpoklßdejme, ₧e chceme demonstrovat postup a rychlost °azenφ pole hodnot podle velikosti n∞kolika r∙zn²mi metodami. Porovnßnφ rychlosti °azenφ r∙zn²mi metodami provedeme nejlΘpe tak, ₧e tyto jednotlivΘ metody naprogramujeme a spustφme souΦasn∞ (toto mßm umo₧nφ vφcevlßknovß aplikace). Musφme vytvo°it vφcevlßknov² objekt, kter² je potomkem t°φdy TThread. V naÜem p°φpad∞ vytvo°φme t°φdu TSortThread. ZaΦneme v²voj novΘ aplikace (zvolφme File | New Application). Pro vytvo°enφ vφcevlßknovΘho objektu zvolφme File | New a na strßnce New vybereme Thread object. Tφm vytvo°φme novou programovou jednotku pro ulo₧enφ vφcevlßknovΘho objektu (musφme zadat jmΘno vytvß°enΘ t°φdy; pou₧ijeme TSortThread). Do tΘto jednotky doplnφme deklaraci naÜφ t°φdy (a jejich potomk∙ pro jednotlivΘ metody °azenφ) podle nßsledujφcφho v²pisu (jednotku p°ejmenujeme na SortThd.cpp). HlaviΦkov² soubor jednotky bude obsahovat:

  10. #ifndef SortThdH
    #define SortThdH
    #include <ExtCtrls.hpp>
    #include <Graphics.hpp>
    #include <Classes.hpp>
    #include <System.hpp>
    //-------------------------------------------------------------------
    extern void __fastcall PaintLine(TCanvas *Canvas, int i, int len);
    //-------------------------------------------------------------------
    class TSortThread : public TThread
    {
    private:
     TPaintBox *FBox;
     int *FSortArray;
     int FSize;
     int FA;
     int FB;
     int FI;
     int FJ;
     void __fastcall DoVisualSwap(void);
    protected:
     virtual void __fastcall Execute(void);
     void __fastcall VisualSwap(int A, int B, int I, int J);
     virtual void __fastcall Sort(int *A, const int A_Size) = 0;
    public:
     __fastcall TSortThread(TPaintBox *Box, int *SortArray,
         const int SortArray_Size);
    };
    //------------------------------------------------------------------
    class TBubbleSort : public TSortThread
    {
    protected:
     virtual void __fastcall Sort(int *A, const int A_Size);
    public:
     __fastcall TBubbleSort(TPaintBox *Box, int *SortArray,
         const int SortArray_Size);
    };
    //------------------------------------------------------------------
    class TSelectionSort : public TSortThread
    {
    protected:
     virtual void __fastcall Sort(int *A, const int A_Size);
    public:
     __fastcall TSelectionSort(TPaintBox *Box, int *SortArray,
         const int SortArray_Size);
    };
    //----------------------------------------------------------------
    class TQuickSort : public TSortThread
    {
    protected:
     void __fastcall QuickSort(int *A, const int A_Size, int iLo, int iHi);
     virtual void __fastcall Sort(int *A, const int A_Size);
    public:
     __fastcall TQuickSort(TPaintBox *Box, int *SortArray,
         const int SortArray_Size);
    };
    //-----------------------------------------------------------------
    #endif
    Funkce PaintLine bude pou₧ita pro zobrazovßnφ postupu °azenφ. Nßsleduje v²pis programovΘ jednotky:
    #include <vcl.h>
    #pragma hdrstop
    #include "sortthd.h"
    void __fastcall PaintLine(TCanvas *Canvas, int I, int Len)
    {
      TPoint points[2];
      points[0] = Point(0, I*2+1);
      points[1] = Point(Len, I*2+1);
      Canvas->Polyline(EXISTINGARRAY(points));
    }
    //----------------------------------------------------------------
    __fastcall TSortThread::TSortThread(TPaintBox *Box, int *SortArray,
        const int SortArray_Size) : TThread(False)
    {
      FBox = Box;
      FSortArray = SortArray;
      FSize = SortArray_Size + 1;
      FreeOnTerminate = True;
    }
    //---------------------------------------------------------------------
    /* Jeliko₧ DoVisualSwap pou₧φvß komponenty VCL nem∙₧e b²t nikdy volßna
       p°φmo vlßknem. DoVisualSwap musφ b²t volßna p°edßnφm metod∞
       Synchronize (DoVisualSwap bude provedeno hlavnφm vlßknem VCL). */
    void __fastcall TSortThread::DoVisualSwap()
    {
      TCanvas *canvas;
      canvas = FBox->Canvas;
      canvas->Pen->Color = TColor(clBtnFace);
      PaintLine(canvas, FI, FA);
      PaintLine(canvas, FJ, FB);
      canvas->Pen->Color = clRed;
      PaintLine(canvas, FI, FB);
      PaintLine(canvas, FJ, FA);
    }
    //-----------------------------------------------------------
    /* VisusalSwap usnad≥uje pou₧φvßnφ DoVisualSwap. Parametry jsou
       p°ekopφrovßny do prom∞nn²ch instance, Φφm₧ je zp°φstupnφme hlavnφmu
       vlßknu VCL kdy₧ provßdφ DoVisualSwap  */
    void __fastcall TSortThread::VisualSwap(int A, int B, int I, int J)
    {
      FA = A;
      FB = B;
      FI = I;
      FJ = J;
      Synchronize(DoVisualSwap);
    }
    //----------------------------------------------------------
    /* Metoda Execute ve volßna p°i spouÜt∞nφ vlßkna */
    void __fastcall TSortThread::Execute()
    {
      Sort(FSortArray, FSize-1);
    }
    //-----------------------------------------------------------
    __fastcall TBubbleSort::TBubbleSort(TPaintBox *Box, int *SortArray,
      const int SortArray_Size):TSortThread(Box, SortArray, SortArray_Size)
    {
    }
    void __fastcall TBubbleSort::Sort(int *A, int const AHigh)
    {
      int I, J, T;
      for (I=AHigh; I >= 0; I--)
        for (J=0; J<=AHigh-1; J++)
          if (A[J] > A[J + 1])
          {
            VisualSwap(A[J], A[J + 1], J, J + 1);
            T = A[J];
            A[J] = A[J + 1];
            A[J + 1] = T;
            if (Terminated) return;
          }
    }
    //--------------------------------------------------------------
    __fastcall TSelectionSort::TSelectionSort(TPaintBox *Box,
      int *SortArray, const int SortArray_Size)
      : TSortThread(Box, SortArray, SortArray_Size)
    {
    }
    void __fastcall TSelectionSort::Sort(int *A, int const AHigh)
    {
      int I, J, T;
      for (I=0; I <= AHigh-1; I++)
        for (J=AHigh; J >= I+1; J--)
          if (A[I] > A[J])
          {
            VisualSwap(A[I], A[J], I, J);
            T = A[I];
            A[I] = A[J];
            A[J] = T;
            if (Terminated) return;
          }
    }
    //--------------------------------------------------------------
    __fastcall TQuickSort::TQuickSort(TPaintBox *Box, int *SortArray,
      const int SortArray_Size)
      : TSortThread(Box, SortArray, SortArray_Size)
    {
    }
    void __fastcall TQuickSort::QuickSort(int *A, int const AHigh, int iLo,
      int iHi)
    {
      int Lo, Hi, Mid, T;
      Lo = iLo;
      Hi = iHi;
      Mid = A[(Lo+Hi)/2];
      do
      {
        if (Terminated) return;
        while (A[Lo] < Mid) Lo++;
        while (A[Hi] > Mid) Hi--;
        if (Lo <= Hi)
        {
          VisualSwap(A[Lo], A[Hi], Lo, Hi);
          T = A[Lo];
          A[Lo] = A[Hi];
          A[Hi] = T;
          Lo++;
          Hi--;
        }
      }
      while (Lo <= Hi);
      if (Hi > iLo) QuickSort(A, AHigh, iLo, Hi);
      if (Lo < iHi) QuickSort(A, AHigh, Lo, iHi);
    }
    void __fastcall TQuickSort::Sort(int *A, int const AHigh)
    {
      QuickSort(A, AHigh, 0, AHigh);
    }
    V tΘto jednotce jsou jednotlivΘ °adφcφ metody deklarovßny jako t°φdy odvozenΘ od TSortThread. Pokuste se pochopit, jak jednotlivΘ objekty vlßken pracujφ. PokraΦovat ve v²voji tΘto aplikace budeme v nßsledujφcφ kapitole.
  11. Zvolte si dalÜφ °adφcφ metodu a p°idejte ji jako dalÜφ vlßkno do naÜφ programovΘ jednotky.
25. Pou₧φvßnφ vlßken I