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.