[delphi] threads: ik snap er niets van...

Pagina: 1
Acties:

  • banaan-X
  • Registratie: Februari 2001
  • Niet online
Ik heb in een programmatje een thread nodig. Ik zal nu niet gaan vertellen wat het programmatje moet kunnen, want dat is wat te uitgebreid, maar laat ik een thread-test-programmatje maken:

Wat ik wil:
- in een hoofdprogramma (mainform) kan iemand met een schuifbalk een waarde tussen 1 en 10 selecteren.
- op de achtergrond draait een functie die deze waarde gebruikt in de opdracht die deze functie heeft.

Stel: de functie zorgt ervoor dat er een plaatje wel/niet op het scherm wordt vertoond (stel een 10x10 pixels plaatjes, dat een LED'je voorstelt) in het interval dat de gebruiker selecteert met de schuifbalk in 1/10 secondes.

Dit knipperen moet dus continue!

Hoe moet dit nou? (graag newbietaal :+ )

  • yodax
  • Registratie: Januari 2000
  • Laatst online: 28-04 08:47
Je kan een globale variabele gebruiken waar je de waarde van de trackbar in opslaat als deze wijzigd. ALLEEN zorgen er voor dat je het het schrijven en lezen naar deze waarde in een criticalsection doet. (zie help voor TCriticalSection)

Een mooiere oplossing zou zijn om een message te posten.

  • banaan-X
  • Registratie: Februari 2001
  • Niet online
Maak je nu wel een thread dan? :?

Ik heb nog even gekeken in de help, maar als ik het voorbeelprogramma in /help/examples/Prgrsbar start, dan loopt dat programma vast. :? Het voorbeeld programma in /demos/thread/ loopt niet vast.


Raar dat Prgrsbar vastloopt, maar goed: hoe werkt nou een thread? Wat moet ik bijv invullen bij "Class name" als ik een thread aanmaak?

  • banaan-X
  • Registratie: Februari 2001
  • Niet online
Ik ben maar gewoon begonnen met het proberen een thread te laten werken.
Wat ik tot nu heb gedaan:

- Scrollbar1 en CheckBox1 op Form1 gegooit.
- New, Thread Object
- als Classname: "thread1"
- thread1.Execute in de threadUnit "Unit1" aangepast in:
code:
1
2
3
4
5
6
7
8
9
10
11
procedure thread1.Execute;
begin
  { Place thread code here }
  repeat
    TForm1.CheckBox1.Checked := true; // dit is regel 36
    Wait(Form1.Scrollbar1.Position);
    TForm1.CheckBox1.Checked := false;
    Wait(Form1.Scrollbar1.Position);

  until false
end;

- in unit van Form1 bij uses "Unit1" opgenomen.
- in form1/unit1 dit opgenomen:
code:
1
2
3
4
procedure TForm1.FormCreate(Sender: TObject);
begin
  thread1.Execute;
end;

Als ik nu wil compileren krijg ik dit:
code:
1
2
3
4
5
6
7
8
[Error] Unit1.pas(36): Undeclared identifier: 'TForm1'
[Error] Unit1.pas(36): Missing operator or semicolon
[Error] Unit1.pas(37): Undeclared identifier: 'Wait'
[Error] Unit1.pas(37): Missing operator or semicolon
[Error] Unit1.pas(37): Missing operator or semicolon
[Error] Unit1.pas(38): Missing operator or semicolon
[Error] Unit1.pas(39): Missing operator or semicolon
[Fatal Error] Uthread.pas(7): Could not compile used unit 'Unit1.pas'

Ik heb ook dit geprobeerd:
code:
1
2
3
4
5
6
7
8
9
10
11
procedure thread1.Execute;
begin
  { Place thread code here }
  repeat
    Form1.CheckBox1.Checked := true;  // dit is regel 36
    Wait(Form1.Scrollbar1.Position);
    Form1.CheckBox1.Checked := false;
    Wait(Form1.Scrollbar1.Position);

  until false
end;

maar dat maakte niets uit.

  • deviltje
  • Registratie: Juli 2000
  • Niet online
je moet ook in het "unit1" uses gedeelte "uses Form1" opnemen.

Iedereen die in telekinese gelooft, steek a.u.b. mijn hand op


  • banaan-X
  • Registratie: Februari 2001
  • Niet online
Op maandag 15 oktober 2001 09:52 schreef deviltje het volgende:
je moet ook in het "unit1" uses gedeelte "uses Form1" opnemen.
Dan krijg ik:

[Fatal Error] Unit1.pas(6): File not found: 'Form1.dcu'

  • banaan-X
  • Registratie: Februari 2001
  • Niet online
Op maandag 15 oktober 2001 10:00 schreef banaan-X het volgende:

[..]

Dan krijg ik:

[Fatal Error] Unit1.pas(6): File not found: 'Form1.dcu'
Wacht, als ik "Uthread" ipv "Form1" doe, dan werkt t geloof ik wel. Ik krijg nu nog alleen nog "[Error] Unit1.pas(37): Undeclared identifier: 'Wait'"

edit:
Maar dat moet ook Sleep zijn natuurlijk. Helaas werkte dat ook niet... Zie post hieronder voor de complete unit

  • banaan-X
  • Registratie: Februari 2001
  • Niet online
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
unit Unit1;

interface

uses
  Classes, Uthread;

type
  thread1 = class(TThread)
  private
    { Private declarations }
  protected
    procedure Execute; override;
  end;

implementation

{ Important: Methods and properties of objects in VCL or CLX can only be used
  in a method called using Synchronize, for example,

    Synchronize(UpdateCaption);

  and UpdateCaption could look like,

    procedure thread1.UpdateCaption;
    begin
    Form1.Caption := 'Updated in a thread';
    end; }

{ thread1 }

procedure thread1.Execute;
begin
  { Place thread code here }
  repeat
    Form1.CheckBox1.Checked := true;
    Form1.IOAccess1.PortB[$278] := Form1.IOAccess1.PortB[$278] or $01;
    Sleep(Form1.Scrollbar1.Position);
    Form1.CheckBox1.Checked := false;
    Sleep(Form1.Scrollbar1.Position);

  until false
end;

Maar op "Sleep(Form1.Scrollbar1.Position);" loopt ie vast: "[Error] Unit1.pas(38): Undeclared identifier: 'Sleep'". Het is ook geen 'identifier' maar een functie...
Als ik Deze functie in de Unit van Form1 opneem geeft ie geen foutmelding. Waarom in Unit2 (de thread) wel?


Vraag 2:
Als ik die Sleep opdrachten even weghaal, dan start het programma nog steeds niet... Hij wil dan niet compileren vanwege deze code in de unit van de form ("[Error] Uthread.pas(31): Undeclared identifier: 'Execute'"):
code:
1
2
3
4
5
procedure TForm1.FormCreate(Sender: TObject);
begin
  thread1.Execute;
  
end;

Zou iemand misschien even de complete source willen bekijken (dus dat ik die even opstuur ofzo)?

  • banaan-X
  • Registratie: Februari 2001
  • Niet online
In 2 uur nog geen reactie? :o :P ;)

  • 9900578Heck
  • Registratie: Juli 2001
  • Laatst online: 21-07 09:24
Ik wil je best ff helpen.

stuur die hele handel maar naar walterheck@ironmaiden.com

ik zie wel wat ik kan doen.

edit:

in 3 minuten wel een reaktie !!! :P :P :P :P

  • banaan-X
  • Registratie: Februari 2001
  • Niet online
Op maandag 15 oktober 2001 12:09 schreef 9900578Heck het volgende:
Ik wil je best ff helpen.

stuur die hele handel maar naar walterheck@ironmaiden.com

ik zie wel wat ik kan doen.

edit:

in 3 minuten wel een reaktie !!! :P :P :P :P
Wow, snel! >:)
(alleen ik niet)
Je kunt de programma's hier downloaden.

1 wil dus niet compileren, en 2 loopt vast bij het starten.

  • 9900578Heck
  • Registratie: Juli 2001
  • Laatst online: 21-07 09:24
je hoort het wel als ik wat weet...

  • banaan-X
  • Registratie: Februari 2001
  • Niet online
Op maandag 15 oktober 2001 12:30 schreef 9900578Heck het volgende:
je hoort het wel als ik wat weet...
OK, je mag dan de aangepaste source (graag wel met commentaar ;) ) ook wel mailen naar mijn e-mailadres (zie mijn info).
Alvast bedankt he.

  • 9900578Heck
  • Registratie: Juli 2001
  • Laatst online: 21-07 09:24
een van de dingen is dat je niet execut meteen moet aanroepen, maar eerst die thread nog moet createn.
zo dus:
code:
1
2
3
4
5
procedure TForm1.FormCreate(Sender: TObject);
var thr1: Thread1;
begin
  thr1:=Thread1.create(false);
end;

dan loopt die thread al, maar ik heb die IOAccess component niet, dus ik kan dat niet testen...

dat eerste project is wel een goede basis, al zou ik in die thread unit die thread1 class naam hernoemen naar TThread1, gewoon omwille van de naamgevingsconventies...

verder is dat sleep een commando dat bij de indy componenten hoort en niet voor andere doeleinden moet gebruikt worden.

laat ff weten of dat al genoeg is...

  • banaan-X
  • Registratie: Februari 2001
  • Niet online
Op maandag 15 oktober 2001 14:16 schreef 9900578Heck het volgende:
een van de dingen is dat je niet execut meteen moet aanroepen, maar eerst die thread nog moet createn.
zo dus:
code:
1
2
3
4
5
procedure TForm1.FormCreate(Sender: TObject);
var thr1: Thread1;
begin
  thr1:=Thread1.create(false);
end;

dan loopt die thread al, maar ik heb die IOAccess component niet, dus ik kan dat niet testen...

dat eerste project is wel een goede basis, al zou ik in die thread unit die thread1 class naam hernoemen naar TThread1, gewoon omwille van de naamgevingsconventies...

verder is dat sleep een commando dat bij de indy componenten hoort en niet voor andere doeleinden moet gebruikt worden.

laat ff weten of dat al genoeg is...
Ik heb in dat 2e programma dit veranderd:
code:
1
2
3
4
5
6
7
procedure TForm1.FormCreate(Sender: TObject);

begin
  thread:=thread.create(true);
  thread.Execute;

end;

Maar hij loopt nog steeds vast bij het opstarten... Zou je misschien een werkende source kunnen mailen? Da's denk ik wat makkelijker, haal dan die IOAccess er maar gewoon uit.

En betreft "Sleep", die heb ik toch echt nodig, en naar mijn weten heb ik nooit Indy geinstalleerd.

Verwijderd

Je moet niet de Execute method van de thread aanroepen, het is juist de bedoeling dat die in een andere thread loopt! Gebruik dus de method Resume om je thread te herstarten.

Als je iets doet zoals
code:
1
MyThread := TThread.Create(True);

hoeft dat niet want dan wordt de thread automatisch gestart.

Dit werkt ook:
code:
1
2
3
MyThread := TThread.Create(False);
..
MyThread.Resume;

De procedure Sleep wordt gedefinieerd in de unit Windows, heb je die in je uses lijst staan?

  • yodax
  • Registratie: Januari 2000
  • Laatst online: 28-04 08:47
Je paste dit stuk code ook maar je hebt het duidelijk niet gelezen:
code:
1
2
3
4
5
6
7
8
9
10
11
{ Important: Methods and properties of objects in VCL or CLX can only be used
  in a method called using Synchronize, for example,

    Synchronize(UpdateCaption);

  and UpdateCaption could look like,

    procedure thread1.UpdateCaption;
    begin
    Form1.Caption := 'Updated in a thread';
    end; }

Je mag een VCL component NOOIT vanuit een thread aanroepen omdat de VCL niet thread safe is. Zie mijn eerste post.
Op maandag 15 oktober 2001 09:31 schreef banaan-X het volgende:
Maak je nu wel een thread dan? :?
Ja, het gaat er om dat je globale variabele (ook VCL componenten dus) niet zomaar mag lezen en schrijven vanuit een thread, omdat de waarde zomaar tussentijds gewijzigd kan worden. Je hele aanpak zals je hem nu hebt is dus fout. Zoals ik al zei, zie mijn eerste post.

  • banaan-X
  • Registratie: Februari 2001
  • Niet online
Op maandag 15 oktober 2001 17:03 schreef yodax het volgende:


Ja, het gaat er om dat je globale variabele (ook VCL componenten dus) niet zomaar mag lezen en schrijven vanuit een thread, omdat de waarde zomaar tussentijds gewijzigd kan worden. Je hele aanpak zals je hem nu hebt is dus fout. Zoals ik al zei, zie mijn eerste post.
Maar ik wil wel zoiets hebben (zie beginpost), hoe moet dan dan?

  • yodax
  • Registratie: Januari 2000
  • Laatst online: 28-04 08:47
Op maandag 15 oktober 2001 17:28 schreef banaan-X het volgende:

[..]

Maar ik wil wel zoiets hebben (zie beginpost), hoe moet dan dan?
Op zondag 14 oktober 2001 21:46 schreef yodax het volgende:
Je kan een globale variabele gebruiken waar je de waarde van de trackbar in opslaat als deze wijzigd. ALLEEN zorgen er voor dat je het het schrijven en lezen naar deze waarde in een criticalsection doet. (zie help voor TCriticalSection)

Een mooiere oplossing zou zijn om een message te posten.
Dus in de main heb je een trackbar, als die zijn waarde wijzigd schrijf je dat naar een global, dit moet je doen in een criticalsection.

Vanuit je thread ga je weer een criticalsection in om de data te lezen, dit doe je alleen aan het begin (of het eind). Dus 1x lezen en locaal kopieren.

Op deze manier haal je dus data van je main naar je thread.

Lees dit stukje ook eens. (gewoon gevonden met google)

Goed nog maar even een voorbeeld van een criticalsection
code:
1
2
3
4
5
6
kritiekeSectie.Enter;
try
    //jouw schrijf of lees acties op globale data
finaly
    kritiekeSectie.Leave;
end;

Verwijderd

IMHO hoef je een globale var van het type integer niet in een critical section te zetten.

  • yodax
  • Registratie: Januari 2000
  • Laatst online: 28-04 08:47
Als je de waarde lokaal kopieert hoeft het idd niet, maar als je nog eens iets anders gaat lezen of schrijven is je code er iig op voorbereid.

Als je het niet lokaal zou kopieren zou je waarde namelijk tijdens je berekening kunnen veranderen. (wat je berekening corrupt kan maken) Als je niet zou kopieren zou je hele thread dus in een criticalsection moeten waardoor je hele effect van je thread weg is.

Synchronize gebruiken voor VCL componenten is iig verplicht.
Pagina: 1