-
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.
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.
-
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.
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:
-
Komponenty datovΘho p°φstupu do databßze jsou vlßknov∞ bezpeΦnΘ, pokud
ka₧dΘ vlßkno mß svoji komponentu Session. Jedinou v²jimkou jsou
ovladaΦe databßze Access (tyto ovladaΦe nejsou vlßknov∞ bezpeΦnΘ). DatovΘ
databßzovΘ ovladaΦe ale nejsou vlßknov∞ bezpeΦnΘ.
-
GrafickΘ objekty jsou vlßknov∞ bezpeΦnΘ. Nenφ tedy nutnΘ pou₧φvat hlavnφ
vlßkno VCL pro p°φstup k TFont, TPen, TBrush, TBitmap,
TMetafile
a TIcon.
-
Zatφmco objekty seznam∙ nejsou vlßknov∞ bezpeΦnΘ, m∙₧eme pou₧φt namφsto
TList
jeho vlßknov∞ bezpeΦnou verzi TThreadList.
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.
-
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.
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.
-
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.
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.
-
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:
#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.
-
Zvolte si dalÜφ °adφcφ metodu a p°idejte ji jako dalÜφ vlßkno do naÜφ programovΘ
jednotky.