-
Když již máme implementovány třídy vláken metodou Execute, můžeme
je použít v naší aplikaci spuštěním kódu v metodě Execute. K použití
vlákna, nejprve vytvoříme instanci třídy vlákna. Instanci vlákna můžeme
vytvořit tak, že je spuštěno bezprostředně nebo jej vytvoříme v klidovém
stavu, které později spustíme voláním metody Resume. Pro vytvoření
vlákna s bezprostředním spuštěním předáme konstruktoru parametr
false.
Např. následující řádek vytváří vlákno a spouští jeho provádění:
TMyThread *SecondProcess
= new TMyThread(false); // vytvoření a spuštění
Nevytvářejte ve své aplikaci mnoho vláken. Doporučený limit je 16 vláken
na proces v jednoprocesorovém systému. Tento limit předpokládá, že většina
těchto vláken bude čekat na externí události. Pokud všechna vlákna jsou
aktivní, pak jich je možno použít méně.
Můžeme vytvořit více instancí stejného typu vlákna pro provádění paralelního
kódu. Např. můžeme vytvořit novou instanci vlákna v reakci na nějakou akci
uživatele umožňující každému vláknu zpracovávat očekávanou reakci.
Množství času CPU věnované vláknu je určeno nastavením priority v konstruktoru.
Můžeme také vytvořit vlákno v klidovém stavu a nastavit prioritu až před
jeho spuštěním:
TMyThread *SecondProcess
= new TMyThread(true);// vytvoření bez spuštění
SecondProcess->Priority
= tpLower; // nastavení priority
SecondProcess->Resume();
// spuštění vlákna
Vlákna mohou být spouštěna a zastavována několikrát před dokončením
provádění. Zastavení vlákna je dočasné a provedeme jej voláním metody Suspend.
Pro opětovné spuštění voláme jeho metodu Resume. Suspend
inkrementuje interní čítač, a tak můžeme vnořovat volání Suspend
a Resume. Vlákno nepokračuje v provádění dokud všechny Suspend
nemají odpovídající Resume.
K trvalému ukončení vlákna voláme metodu Terminate. Tato metoda
nastavuje vlastnost Terminated vlákna na true. Pokud metoda
Execute
je správně implementována, je periodicky testována vlastnost
Terminated
a ukončuje provádění, když je true.
Když naše aplikace vyžaduje více instancí stejné třídy vlákna (pro
opakované spouštění), pak můžeme zvýšit výkonnost odkládáním vláken pro
opětovné použití (namísto jejich rušení a opětovného vytváření). Pro odkládání
vláken, musíme udržovat seznam vytvořených vláken. Tento seznam může být
udržován objektem, který používá vlákna nebo můžeme použít globální proměnnou
k uložení odloženého vlákna.
Když je požadováno nové vlákno, pak je použito odložené vlákno nebo
vytvořeno nové vlákno (viz následující funkce):
TThreadList *pCache;
...
TThread *GetThread(void)
{
TList *pList
= pCache->LockList();
TThread *NewThread;
if (pList->Count)
// žádné vlákno není odloženo
{
NewThread = (TThread *)pList->Items[0];
pList->Delete(0); // odstranění ze seznamu
}
else
NewThread=(TThread *)new TMyThread(true);//vytvoření ale nespuštění
pCache->UnlockList();
return NewThread;
}
Jestliže vlákno končí provádění je odloženo (obsluha OnTerminate):
void __fastcall TMyThread::Terminate(TObject
*Sender)
{
pCache->Add(&this);
}
Když aplikace končí provádění (nebo objekt, který vlastní vlákna je
rušen), pak odložená vlákna musí být uvolněna (obsluha OnDeactivate
aplikace):
void __fastcall TDataModule1::ApplicationDeactivate(TObject
*Sender)
{
TList *pList
= pCache->LockList();
for (int i
= pList->Count - 1; i >= 0; i--)
{
delete pList->Items[i];
pList->Delete(i);
}
pCache->UnlockList();
}
-
Pokud ladíme vícevláknové aplikace, pak můžeme sledovat
stav všech současně prováděných vláken nebo určit které vlákno bude prováděno,
když provádění je zastaveno bodem přerušení. Můžeme použít dialogové okno
Stavu vláken k manipulaci s vlákny v naší aplikaci. Toto okno zobrazíme
volbou View | Threads. Když nastane ladící událost (bod přerušení,
provádění) pak zobrazení stavu vlákna indikuje stav každého vlákna. V místní
nabídce okna jsou volby k lokalizaci odpovídajícího zdroje nebo k udělání
jiného vlákna aktuálním. Jestliže vlákno je označeno jako aktuální, pak
následující krok nebo operace je relativní k tomuto vláknu. Vlákna jsou
identifikována identifikačními čísly (hodnota vlastnosti ThreadID).
-
Vrátíme se k naší aplikaci z předchozí kapitoly.
Formulář aplikace zvětšíme a nastavíme u něj tyto vlastnosti: BorderStyle
na bsDialog a Caption na Demonstrace řazení. Na formulář
přidáme tři komponenty Bevel a nastavíme u nich vlastnosti Top
na 24, Width na 177 a Height na 233.
U první z nich nastavíme Left na 8, u druhé na 192
a třetí na 376. Do každé z těchto komponent vložíme komponentu PaintBox
a nastavíme u nich stejné hodnoty vlastností jako u komponent Bevel.
Levou z těchto komponent nazveme BubbleSortBox, prostřední SelectionSortBox
a pravou QuickSortBox. Nad každou z těchto komponent vložíme Label
s texty: Bublinková metoda, Řazení výběrem a Quick Sort.
Do spodní části formuláře vložíme ještě tlačítko s textem Začni řadit.
Tím je tento formulář hotov. Do deklarace formuláře vložíme další deklarace
(viz následující výpis hlavičkového souboru formuláře):
#ifndef ThSortH
#define ThSortH
#include <StdCtrls.hpp>
#include <ExtCtrls.hpp>
#include <Dialogs.hpp>
#include <Forms.hpp>
#include <Controls.hpp>
#include <Graphics.hpp>
#include <Classes.hpp>
#include <SysUtils.hpp>
#include <Messages.hpp>
#include <Windows.hpp>
#include <System.hpp>
class TThreadSortForm
: public TForm
{
__published:
TButton *StartBtn;
TPaintBox *BubbleSortBox;
TPaintBox *SelectionSortBox;
TPaintBox *QuickSortBox;
TLabel *Label1;
TBevel *Bevel1;
TBevel *Bevel2;
TBevel *Bevel3;
TLabel *Label2;
TLabel *Label3;
void __fastcall
BubbleSortBoxPaint(TObject *Sender);
void __fastcall
SelectionSortBoxPaint(TObject *Sender);
void __fastcall
QuickSortBoxPaint(TObject *Sender);
void __fastcall
FormCreate(TObject *Sender);
void __fastcall
StartBtnClick(TObject *Sender);
private:
int ThreadsRunning;
void __fastcall
RandomizeArrays(void);
void __fastcall
ThreadDone(TObject *Sender);
public:
void __fastcall
PaintArray(TPaintBox *Box,const int *A,
const int A_Size);
virtual __fastcall
TThreadSortForm(TComponent *Owner);
};
//--------------------------------------------------------------------
typedef int TSortArray[115];
typedef TSortArray
*PSortArray;
//--------------------------------------------------------------------
extern PACKAGE TThreadSortForm
*ThreadSortForm;
extern bool ArraysRandom;
extern int BubbleSortArray[115];
extern int SelectionSortArray[115];
extern int QuickSortArray[115];
#endif
Následuje výpis jednotky formuláře:
#include <vcl.h>
#pragma hdrstop
#include <stdlib.h>
#include "thsort.h"
#include "sortthd.h"
#pragma resource
"*.dfm"
TThreadSortForm *ThreadSortForm;
//--------------------------------------------------------------
Boolean ArraysRandom;
TSortArray BubbleSortArray,
SelectionSortArray, QuickSortArray;
__fastcall TThreadSortForm::TThreadSortForm(TComponent
*Owner)
: TForm(Owner)
{
}
void __fastcall TThreadSortForm::PaintArray(TPaintBox
*Box,
int const *A, int const ASize)
{
int i;
TCanvas *canvas;
canvas = Box->Canvas;
canvas->Pen->Color
= clRed;
for (i=0;
i < ASize; i++)
PaintLine(canvas, i, A[i]);
}
//---------------------------------------------------------------
void __fastcall TThreadSortForm::BubbleSortBoxPaint(TObject
*)
{
PaintArray(BubbleSortBox,
EXISTINGARRAY(BubbleSortArray));
}
void __fastcall TThreadSortForm::SelectionSortBoxPaint(TObject
*)
{
PaintArray(SelectionSortBox,
EXISTINGARRAY(SelectionSortArray));
}
void __fastcall TThreadSortForm::QuickSortBoxPaint(TObject
* /*Sender*/)
{
PaintArray(QuickSortBox,
EXISTINGARRAY(QuickSortArray));
}
//---------------------------------------------------------------
void __fastcall TThreadSortForm::FormCreate(TObject
* /*Sender*/)
{
RandomizeArrays();
}
void __fastcall TThreadSortForm::StartBtnClick(TObject
* /*Sender*/)
{
TBubbleSort
*bubble;
TSelectionSort
*selsort;
TQuickSort
*qsort;
RandomizeArrays();
ThreadsRunning
= 3;
bubble = new
TBubbleSort(BubbleSortBox,
EXISTINGARRAY(BubbleSortArray));
bubble->OnTerminate
= ThreadDone;
selsort =
new TSelectionSort(SelectionSortBox,
EXISTINGARRAY(SelectionSortArray));
selsort->OnTerminate
= ThreadDone;
qsort = new
TQuickSort(QuickSortBox, EXISTINGARRAY(QuickSortArray));
qsort->OnTerminate
= ThreadDone;
StartBtn->Enabled
= False;
}
//---------------------------------------------------------------------
void __fastcall TThreadSortForm::RandomizeArrays()
{
int i;
if (! ArraysRandom)
{
Randomize();
for (i=0; i < ARRAYSIZE(BubbleSortArray); i++)
BubbleSortArray[i] = random(170);
memcpy(SelectionSortArray, BubbleSortArray,
sizeof(SelectionSortArray));
memcpy(QuickSortArray, BubbleSortArray, sizeof(QuickSortArray));
ArraysRandom = True;
Repaint();
}
}
//---------------------------------------------------------------------
void __fastcall TThreadSortForm::ThreadDone(TObject
* /*Sender*/)
{
ThreadsRunning--;
if (! ThreadsRunning)
{
StartBtn->Enabled = True;
StartBtn->SetFocus();
ArraysRandom = False;
}
}
Pokuste se podle tohoto výpisu vytvořit běžící
aplikaci a snažte se pochopit jednotlivé činnosti. Snažte se pochopit jak
pracují vícevláknové aplikace.
-
Přidejte do předchozí aplikace demonstraci dalšího
způsobu řazení (vlákno, které jste vytvořili na závěr předchozí kapitoly).