Säikeet (=threads) eivät oikeastaan kuulu kieleen, vaan käyttöjärjestelmään. Koska Delphitoimii kuitenkin vain Windowsin alla, käsitellään tässä säikeet ikäänkuin kieleen kuuluvana asiana. Säikeet toimivat Win95:sta alkaen. Vertailukohdaksi otetaan Borland C++ 5.0, OWL 5.0 ja AppExpertillä osittain tehty sovellus.
Seuraava esimerkki luo dialogi- ikkunassa (lomakkeessa, form) valmiiksi olevan Käynnistä- näppäimen lisäksi joukon laskureita (Delphissä tekstikenttiä ja C++:ssa Tbutton- nappuloita). Kun Käynnistä- nappia painetaan, käynnistetään kutakin laskuri- kenttää varten oma prosessi (säie), joka pyörittää kentässä lukuja 0:sta ylöspäin. Kenttää painamalla voidaan ko. säie "tappaa". Kummastakin ohjelmasta on jätetty listaamatta sekä pääohjelma että resurssitiedosto.
#include <owl/button.h>
#include <classlib/thread.h>
#include "saieapp.rh" // Def of all resources
const int SAIKEITA = 20; // Säikeiden lkm
const int PAIVITYS = 100000;// Minkä väl.päiv.
const int KIERROKSIA = 1000000;
class cLaskuri : public TThread
// Laskurisäie perit.yleisestä säikeestä
// ja siihen lisätään omat erikoispiirt.
{
TControl *Text; //Mihin teksti-ikkunaan
int n; //Sis. laskurin arvo
int raja; //Mihin asti lasketaan
protected:
void count(); //Yhden laskuaskeleen suor
//Perit.luokan Run korvataan omalla
int Run();
public:
//Rakentaja, joka alustaa mm. sis. muut.
cLaskuri(TControl *oLabel,int r) :
Text(oLabel), raja(r), n(0) {}
~cLaskuri() { ;}
};
//{{TDialog = TFormSaieDemo}}
class TFormSaieDemo : public TDialog {
// Lomake,jossa laskureita pyöritetään
TControl *Labels[SAIKEITA];
cLaskuri *saikeet[SAIKEITA];
public:
TFormSaieDemo(TWindow* parent,
TResId resId = IDD_SAIEDEMO,
TModule* module = 0);
virtual ~TFormSaieDemo();
//{{TFormSaieDemoVIRTUAL_BEGIN}}
public:
virtual bool Create();
virtual TResult EvCommand(uint id,
THandle hWndCtl, uint notifyCode);
virtual bool CanClose();
//{{TFormSaieDemoVIRTUAL_END}}
//{{TFormSaieDemoRSP_TBL_BEGIN}}
protected:
void BNKaynnistaClicked();
//{{TFormSaieDemoRSP_TBL_END}}
DECLARE_RESPONSE_TABLE(TFormSaieDemo);
}; //{{TFormSaieDemo}}
#include <owl/pch.h>
#include "saiedemo.h"
#define ALKU_ID 3000
//-------------------------------------------
// cLaskuri =================================
//-------------------------------------------
//-------------------------------------------
void cLaskuri::count()
{
n++;
if ( n % PAIVITYS == 0 ) {
char s[30];
wsprintf(s,"%d",n);
Text->SetWindowText(s);
}
}
//-------------------------------------------
int cLaskuri::Run()
// Tämä suorittaa varsinaisen säikeen
// toimenpiteet. Kun tämä metodi loppuu,
// pysähtyy säie.
{
const char *mes="";
while ( n < raja ) {
if ( ShouldTerminate() ) {
mes = "T"; break;
}
count();
}
char s[30];
wsprintf(s,"%d%s",n,mes);
Text->SetWindowText(s);
return 0;
}
//-------------------------------------------
// TFormSaieDemo ============================
//-------------------------------------------
//-------------------------------------------
DEFINE_RESPONSE_TABLE1(TFormSaieDemo,TDialog)
//{{TFormSaieDemoRSP_TBL_BEGIN}}
EV_BN_CLICKED(IDKAYNNISTA,
BNKaynnistaClicked),
//{{TFormSaieDemoRSP_TBL_END}}
END_RESPONSE_TABLE;
//{{TFormSaieDemo Implementation}}
//-------------------------------------------
TFormSaieDemo::TFormSaieDemo(TWindow* parent,
TResId resId, TModule* module)
: Tdialog(parent, resId, module)
{
}
//-------------------------------------------
TFormSaieDemo::~TFormSaieDemo()
{
Destroy();
}
//-------------------------------------------
static bool Clear(cLaskuri * &saie)
// Tämä funktio tarkistaa onko säie jo
// siivottu, eli se on tuhottu.
// Mikäli säie ei ole tuhottu, mutta se
// on valmis, tuhotaan säie.
{
if ( saie == NULL ) return true;
if ( saie->GetStatus() !=
TThread::Finished ) return false;
delete saie;
saie = NULL;
return true;
}
//-------------------------------------------
void TFormSaieDemo::BNKaynnistaClicked()
{
for (int i=0; i<SAIKEITA; i++) {
if ( !Clear(saikeet[i]) ) continue;
saikeet[i] = new cLaskuri(Labels[i],
KIERROKSIA);
saikeet[i]->Start();
}
saikeet[0]->SetPriority(
THREAD_PRIORITY_ABOVE_NORMAL);
}
//-------------------------------------------
TResult TFormSaieDemo::EvCommand(uint id,
THandle hWndCtl, uint notifyCode)
// Jos laskuria klik., "tapetaan vast.säie"
{
TResult result=TDialog::EvCommand(id,
hWndCtl,notifyCode);
int i = id-ALKU_ID;
if ( 0 <= i && i < SAIKEITA )
if ( !Clear(saikeet[i]) )
saikeet[i]->Terminate();
return result;
}
//-------------------------------------------
bool TFormSaieDemo::Create()
// Luodaan laskurit alekkain näytölle
{
bool result;
int y = 10, dy = 20;
for (int i=0; i<SAIKEITA; i++) {
saikeet[i] = NULL;
Labels[i] = new Tbutton(this,ALKU_ID+i,
"Terve",10,y,70,dy);
y += dy;
}
result = Tdialog::Create();
TRect rc = this->GetWindowRect();
rc.top = 0; rc.bottom = rc.top + y + 50;
this->MoveWindow(rc,TRUE);
return result;
}
bool TFormSaieDemo::CanClose()
{
bool result = TDialog::CanClose();
for (int i=0; i<SAIKEITA; i++) {
if ( Clear(saikeet[i]) ) continue;
result = false;
saikeet[i]->Terminate();
}
return result;
}
saie\saiedemo.pas - esimerkki säikeistä
unit saiedemo;
interface
uses
Windows, Messages, SysUtils, Classes,
Graphics, Controls, Forms, Dialogs,
StdCtrls;
const SAIKEITA = 20; // Säikeiden lkm
PAIVITYS = 100000;// Minkä välein päiv.
KIERROKSIA = 1000000;
type
//-----------------------------------------
cLaskuri = class(TThread)
// Laskurisäie perit.yleisestä säikeestä
// ja siihen lisätään omat erikoispiirt.
private
Text : TLabel; //Mihin teksti-ikkunaan
n:integer; //Sis. laskurin arvo
raja:integer; //Mihin asti lasketaan
protected
procedure count; //Yhden laskuaskeleen su
//Perit.luokan Execute korvataan omalla
procedure Execute; override;
public
//Rakentaja, joka alustaa mm. sis. muut.
constructor Create(oLabel:TLabel;
r:integer);
end;
//-----------------------------------------
TFormSaieDemo = class(TForm)
// Lomake,jossa laskureita pyöritetään
ButtonKaynnista: TButton;
procedure ButtonKaynnistaClick(
Sender: TObject);
procedure ThreadDone(Sender:TObject);
procedure FormCreate(Sender: TObject);
procedure LabelsClick(Sender: TObject);
private
{ Private declarations }
Labels: Array [0..SAIKEITA-1] of TLabel;
saikeet:Array[0..SAIKEITA-1] of cLaskuri;
public
{ Public declarations }
end;
//-------------------------------------------
// Globaalit muuttujat
//-------------------------------------------
var
FormSaieDemo: TFormSaieDemo;
implementation
//-------------------------------------------
// cLaskuri =================================
//-------------------------------------------
//-------------------------------------------
procedure cLaskuri.count;
begin
inc(n);
if ( n mod PAIVITYS = 0 ) then
Text.Caption := IntToStr(n);
end;
//-------------------------------------------
procedure cLaskuri.Execute;
// Tämä suorittaa varsinaisen säikeen
// toimenpiteet. Kun tämä metodi loppuu,
// pysähtyy säie. Säie häviää autom.
// jos FreeOnTerminate := True
var mes:String;
begin
mes := '';
while ( n < raja ) do begin
if terminated then begin
mes := 'T'; Break;
end;
count;
end;
Text.Caption := IntToStr(n)+mes;
end;
//-------------------------------------------
constructor cLaskuri.Create(oLabel:TLabel;
r:integer);
begin
inherited Create(False);
Text := oLabel;
raja := r;
n := 0;
FreeOnTerminate := True;
end;
{$R *.DFM}
//-------------------------------------------
// TformSaieDemo ============================
//-------------------------------------------
//-------------------------------------------
procedure TFormSaieDemo.ThreadDone(
Sender:TObject);
// Tämä tapahtuma, kun jokin säie valmistuu.
var i:integer;
begin
for i:=0 to SAIKEITA-1 do // Etsitään säie
if ( Sender = saikeet[i] ) then
saikeet[i] := NIL;
end;
//-------------------------------------------
procedure TFormSaieDemo.ButtonKaynnistaClick(
Sender: TObject);
var i:integer;
begin
// Luodaan uudet säikeet niiden tilalle
// joita ei ole
for i:=0 to SAIKEITA-1 do
if ( saikeet[i] = NIL ) then begin
saikeet[i] :=
cLaskuri.Create(Labels[i],KIERROKSIA);
saikeet[i].OnTerminate := ThreadDone;
end;
saikeet[0].Priority := tpHigher;
end;
//-------------------------------------------
procedure TFormSaieDemo.LabelsClick(
Sender:TObject);
// Jos laskuria klik., "tapetaan vast.säie"
var i:integer;
begin
i := (Sender as TLabel).Tag;
if ( saikeet[i] <> NIL ) then
saikeet[i].Terminate;
end;
//-------------------------------------------
procedure TFormSaieDemo.FormCreate(
Sender: TObject);
// Luodaan laskurit alekkain näytölle
var i,y,dy:integer;
begin
y := 10; dy := 20;
for i:=0 to SAIKEITA-1 do begin
saikeet[i] := NIL;
Labels[i] := TLabel.Create(Self);
Labels[i].AutoSize := False;
Labels[i].top := y;
Labels[i].left := 10;
Labels[i].Width := 70;
Labels[i].Caption := 'Terve';
Labels[i].Parent := Self;
Labels[i].Tag := i;
Labels[i].OnClick := LabelsClick;
y := y + dy;
end;
Top := 0; Height := y + 50;
end;
end.
-
- * Delphissä säikeen loppumisesta saadaan selvä
viesti. Tämä helpottaa pysähtyneiden säikeiden poistamista
tai muuta valmistuneen prosessin jatkokäsittelyä
- Delphissä ohjelman voi kokeilujen perusteella turvallisesti
sammuttaa vaikka säikeiden suoritus olisikin kesken. C++:ssa
täytyy sammuttaminen estää, mikäli säikeitä on
käynnissä ja suorittaa sammutus loppuun vasta kun säikeet ovat
loppuneet
- Delphissä on helpompi laittaa säie käyntiin jo
konstruktorissa, koska tätä explisiittisesti kuitenkin kutsutaan:
C++:ssa voi olla myös staattinen olio ja tällöin
säie lähtisi käyntiin jo staattisen olion esittelyn
yhteydessä
- Delphissä dynaamisten kontrollien
tekeminen on jossain määrin helpompaa, kun
viestinkäsittelijä voidaan sijoittaa kontrolliin jo luontivaiheessa
- vaikka dynaamiset kontrollit onkin itse luotu, häviävät ne
automaattisesti lomakkeen poistuessa, koska kontrollit ovat lomakkeen lapsia
- luotaessa kontrolleja dynaamisesti, kannattaa kontrollin Tag-
ominaisuuteen laittaa jokin kontrollia hyvin kuvaava arvo. Edellisessä
esimerkissä tämä on ollut kontrollin järjestysnumero
- Delphissä on huomattavasti helpompi muuttaa kontrollin
tiettyä ominaisuutta. Toisaalta jos muutetaan kerralla esim. paikka ja
kokoa, on C++:n suorakaiteen välittäminen
järkevämpää, koska jokaisesta yksittäisestä
sijoituksesta tulee periaatteessa muutostarve näytöllä
- Delphin merkkijonoja on huomattavasti helpompi
käyttää kuin C:n perusmerkkijonoja, joiden
päälle Windows ja myös OWL- luokkakirjasto on
suurelti rakennettu
Tehtävä 2.18 TLabel => TButton
- Muuta saiedemo.pas- esimerkissä laskurikentät nappuloiksi.