Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Thread Anfängerfrage (https://www.delphipraxis.net/12179-thread-anfaengerfrage.html)

Spurius 22. Nov 2003 14:09


Thread Anfängerfrage
 
Hallo,
ich habe amgefangen einen Netzwerkchat zu programmieren, und bin zu der Erkenntnis gekommen, dass ich threads brauche.
Da ich mich mit threads nicht auskenne, funktioniert folgender Code nicht;
Delphi-Quellcode:
unit thread;

interface

uses
  Classes;

type
  mythread = class(TThread)
  private
    { Private-Deklarationen }
  protected
    procedure Execute; override;
    constructor create;
  public
   procedure lesen;    // lesen soll später nachrichten aus einer tcp connection lesen
  end;

implementation

{ Wichtig: Methoden und Eigenschaften von Objekten in visuellen Komponenten dürfen
  nur in einer Methode namens Synchronize aufgerufen werden, z.B.

      Synchronize(UpdateCaption);

  und UpdateCaption könnte folgendermaßen aussehen:

    procedure mythread.UpdateCaption;
    begin
      Form1.Caption := 'Aktualisiert in einem Thread';
    end; }

{ mythread }
constructor mythread.create;
begin
 inherited create(false);
end;
procedure mythread.lesen;
var test:string;
begin
 test := client.readln;  //client ist ein tcpclient auf der form, wo ich den thread verwenden will
 memo1.lines.add(test);  //memo1 ist ein memo auf der form wo ich den thread verwenden will
end;
procedure mythread.Execute;
begin
  { Thread-Code hier einfügen }
  lesen;
end;

end.
Also,
der client ist nicht bekannt, wie muss ich das machen
gleiches gilt für memo1

Gruß
Spurius

Luckie 22. Nov 2003 14:12

Re: Thread Anfängerfrage
 
Nimm die Unit der Form in die Thread Unit auf. :roll:

Spurius 22. Nov 2003 14:23

Re: Thread Anfängerfrage
 
hab ich gemacht, aber dann kommt
[Fataler Fehler] modul.pas(8): Überkreuzender Bezug zweier Units auf 'modul'

und in der hilfe steht, das man da wenn möglich in der implementation machen soll(oder so),
und dann gehts auch nicht.

Gruß
Spurius

Spurius 22. Nov 2003 14:25

Re: Thread Anfängerfrage
 
ok, jetzt kommt die fehlermeldung nicht mehr :)
mal sehen, wie's weitergeht ...
Gruß
Spurius

Spurius 22. Nov 2003 14:31

Re: Thread Anfängerfrage
 
Schon wieder ne Frage :roll:

Wie kann ich mythread jetzt in der hauptunit (name: modul) verwenden?

muss ich da auch den constructor aufrufen?

kann mir vielleicht jemand den code posten, wie ich mythread da einbaue?

Gruß

Spurius

choose 22. Nov 2003 14:41

Re: Thread Anfängerfrage
 
Hallo Spurius,

Du hast zunächst das Problem, dass Dein Thread-Objekt (unabhängig davon, dass es sich um einen Thread handelt) keine Kenntnis von client hat, stattdessen wird dies wahrscheinlich eine Exemplarvariable von Deiner Form-Klasse sein.
Auch wenn Du nun die Unit des Forms einbinden solltes, wüsstest Du zwar von der Formular-Klasse, aber nicht, welches Formular-Exemplar (schließlich könnten mehrere Formulare der selben Klasse erzeugt werden) nun tatsächlich verwendet werden soll.
Darüber hinaus hast Du einen entscheidenen Kommentar mit in Deiner Unit: "Niemals VCL-Controls aus einem Thread heraus verwenden". Deine Änderung des Memos verstößt gegen diese Forderung...

Eine Mögliche Lösung soll diese Pseudocode verdeutlichen
Delphi-Quellcode:
type
  TMyThreadClass = class(TThread)
  private
    FClient : TSomeClient;
    FMemo : TSomeVCLControl;
    procedure UpdateMemo;
  protected
    procedure Execute;override;
  public
    constructor Create(const AClient: TSomeClient;
      const AMemo: TSomeVCLControl);
  end;

constructor TMyThreadClass.Create(const AClient: TSomeClient;
  const AMemo: TSomeVCLControl);
begin
  inherited Create(True);
  Assert(Assigned(AClient));
  Assert(Assigned(AMemo));

  //copy references to member vars
  FClient:= AClient;
  FMemo:= AMemo;
  //start thread
  Resume;
end;

procedure TMyThreadClass.Execute;
begin
  while not Terminated do
    if FClient.HasData then
      //update vcl controls only within the mainthread!
      Synchronize(UpdateMemo);
    else
      //better: use some syncobj connected with
      //   FClient to wait for!
      Sleep(100);
end;

procedure TMyThreadClass.UpdateMemo;
begin
  //will be executed within the mainthread
  FMemo.Lines.Append(FClient.ReadLn);
end;
Die Hauptgedanken dabei:
  • Der Thread bekommt die Referenzen auf die zu verwendenen Objekte übergeben und verwendet sie dort (besser noch: die Connection (FClient ist nur ihm bekannt)
  • Das Aktualisieren des GUIs findet im Haupthread statt (unter Verwendung von Synchronize)

Erzeugt werden könnte Dein Thread innerhalb des Formulars dann wie folgt:
Delphi-Quellcode:
Self.FThread:= TMyThreadClass.Create(Self.Client1, Self.Memo1);

Spurius 22. Nov 2003 14:53

Re: Thread Anfängerfrage
 
Hallo,
ich hab zu demo Code ein paar Fragen:

1. FClient : TSomeClient; tsomeclient, tsomevclcontrol sind feste Ausdrücke, nicht nur Bsp. ?
FMemo : TSomeVCLControl;

2. public
constructor Create(const AClient: TSomeClient; heisst dass, das irgendéin client, bzw. memo
const AMemo: TSomeVCLControl); erzeugt wird, dem ich dann später einen
vorhandenen client zuweisen kann?

3. constructor TMyThreadClass.Create(const AClient: TSomeClient;
const AMemo: TSomeVCLControl);
begin
inherited Create(True); wenn ich hier create(false) eingebe, dann brauch ich später kein resume?
Assert(Assigned(AClient)); was machen die 2 zeilen?
Assert(Assigned(AMemo));

//copy references to member vars
FClient:= AClient; wird hier dem beliebigen client der vorhandene zugewiesen?
FMemo:= AMemo;
//start thread
Resume;
end;

4. procedure TMyThreadClass.Execute; den abschnitt versteh ich nicht
begin
while not Terminated do
if FClient.HasData then
//update vcl controls only within the mainthread!
Synchronize(UpdateMemo);
else
//better: use some syncobj connected with
// FClient to wait for!
Sleep(100);
end;

So, ich will hier nicht nerven, aber ich denke, es ist besser was zu verstehen als nur den Code abzutippen.

Gruß

Spurius

Rumpi 22. Nov 2003 15:03

Re: Thread Anfängerfrage
 
Hi,

suche doch mal nach TTimerThread im Forum,
ich galaube ich habe da auch ein Beispiel
geschrieben.

Zur Info:
Eine IP Verbindung immer aus dem Haupthread
"ThreadSync" mit
Delphi-Quellcode:
 
Synchronize( DoConnect );
aufbauen.

Ich bin gestern "mal wieder drüber" gestolpert.

Ich habe TTimerThread in alten code eingebaut
um eine IP Verbindung aus dem Thread heraus
aufzubauen (ohne Synchronize ) und den Status
zu überwachen, Reconnect ect.

Ich habe dann in die TTimerThread class noch
Delphi-Quellcode:
TSyncMethod = (smThreadSync, smNone);
...
FSyncMethod: TSyncMethod;
...

...
  if FSyncMethod = smNone then
    Notify
  else
    Synchronize( Notify );
Bitte Info wenn du die neue haben möchtest.


mfg Rumpi

choose 22. Nov 2003 15:11

Re: Thread Anfängerfrage
 
Hui, das wird wohl 'ne lange Antwort :gruebel:...

Zitat:

FClient : TSomeClient; tsomeclient, tsomevclcontrol sind feste Ausdrücke, nicht nur Bsp. ?
FMemo : TSomeVCLControl;
Das sind natürlich nur Beispielklassen, ich wollte mit Ihnen nur verdeutlichen, dass es sich
  • bei dem Memo um ein VCL-Objekt handelt
  • bei dem Client um einen beliebigen Client handelt, wobei auch die unten verwendeten Methodenbezeichner der Phantasie entsprechen

Zitat:

Delphi-Quellcode:
constructor Create(const AClient: TSomeClient;
heisst dass, das irgendéin client, bzw. memo
Delphi-Quellcode:
const AMemo: TSomeVCLControl);
erzeugt wird, dem ich dann später einen vorhandenen client zuweisen kann?
Hier wird nur etwas bereits erzeugtest (zB im Formular vorhandenes) an das neue Exemplar der Thread-Klasse übergeben.
Zitat:

Delphi-Quellcode:
Assert(Assigned(AClient));
Assert(Assigned(AMemo));
was machen die 2 zeilen?
Ein Blick in die OH verrät: Hier prüfen wir, ob wirklich etwas übergeben wordne ist, oder nicht vielleicht doch nil. Im letzteren Fall würde eine spezielle Exception geworfen werden. Assertions dienen nur der Absicherung, und
  • deuten zum einen an, was man in den folgenden Zeilen vorraussetzt und
  • prüfen (sofern aktiviert) ob es auch wirklich stimmt (Stichwort: ProgrammingByContract, Assertions).
Zitat:

Delphi-Quellcode:
inherited Create(True);
wenn ich hier create(false) eingebe, dann brauch ich später kein resume?
theoretisch: Ja. Praktisch solltest Du den Thread erst "loslaufen lassen", wenn alles, was in Execute verwendet wird, initialisiert ist (hier: die Exemplarvariablen).

Zitat:

4. procedure TMyThreadClass.Execute; den abschnitt versteh ich nicht
Delphi-Quellcode:
begin
while not Terminated do
if FClient.HasData then
//update vcl controls only within the mainthread! 
Synchronize(UpdateMemo);
else
//better: use some syncobj connected with
// FClient to wait for! 
Sleep(100);
end;

Threads befinden sich iA in einer Endlosschleife (siehe div. Tuts hierzu). In Worten passiert hier folgendes: "So lange Du nicht beendet bist, prüfe ob neue Daten beim Client vorliegen. Falls nicht, kurz schlafen legen. Fall doch, rufe die Methode UpdateMemo innerhalb des Hauptthreads auf."

Ich hoffe, dass ich Dir mit diesen Ausführungen etwas weiter helfen konnte.

Spurius 22. Nov 2003 15:50

Re: Thread Anfängerfrage
 
Hallo,
ich hab leider immer noch nicht alles verstanden.
Was kann ich denn konkret einsetzten:
Delphi-Quellcode:
FClient : TSomeClient;
    FMemo : TSomeVCLControl;
für tsomeclient, tsome vclcontrol?
Gruß
Spurius

choose 22. Nov 2003 16:01

Re: Thread Anfängerfrage
 
Hallo Spurius,

das hängt davon ab, was Du verwendest. Ich denke, dass Du mit TMemo und zB TidTCPClient oä arbeitest? Im zweifelsfall kann Du ads im OI nachsehen, sofern sich die Komponenten in einer Form befinden.
Bitte lies auch in der Hilfe zu der Klient-Komponete, die Du verwendest, ob sie Threadsicher (nicht nur -fähig!) ist, andernfalls müsstest Du die Aufrufe dieser Komponete ebenfalls synchronisieren...
Alles halb so wild ;)

Spurius 22. Nov 2003 16:32

Re: Thread Anfängerfrage
 
Hallo,
ja ich verwende tidtcpclient, aber wenn ich im thread

client: tidtcpclient; schreibe, kommt die meldung, dass das unbekannt sei

Gruß
Spurius

choose 22. Nov 2003 16:58

Re: Thread Anfängerfrage
 
Sinngemäß aus der OH: Du kannst nur Typen verwenden, die zuvor deklariert wurden.

In Deinem Fall ist die Klasse TidTCPClient in einer Indy-Unit deklariert, die Du einbinden musst. Bei der Arbeit mit Formularen geschieht dies beim Platzieren einer Komponente auf einer Form oder einem Datenmodul automatisch. Wirf mal einen Blick in den uses-Abschnitt der Unit Deiner Formular-Klasse.

Kamil 22. Nov 2003 19:34

Re: Thread Anfängerfrage
 
Hi,
der Vorschlag von Choose wird zwar funktionieren, aber er ist meiner Meinung nicht so gut.
Ich würde es so machen:
Nur für den Zugriff auf TMemo und ähnliche Komponenten Synchronize verwenden, da dieser Code im Mainthread ausgeführt wird. (nur das notwendigste mit synchronize ausführen)
Dein TIdTCPClient sollte zum Thread gehören (d.h. im Thread erstellen, initalisieren und am Ende freigeben), damit er im Thread arbeiten kann und nicht im Mainthread arbeiten muss. Wenn du (fast) alles im Mainthread ausführst, dann kannst du dir den zweiten Thread gleich sparen...


Ungefähr so würde ich es machen:
Delphi-Quellcode:
type
  TMyThreadClass = class(TThread)
  private
    FClient : TSomeClient;
    FMemo : TSomeVCLControl;
    procedure UpdateMemo;
  protected
    s: string;
    procedure Execute;override;
  public
    constructor Create(Host: string; Port: integer; const AMemo: TSomeVCLControl);
  end;


constructor TMyThreadClass.Create(Host: string; Port: integer; const AMemo: TSomeVCLControl);
begin
  inherited Create(True);
  Assert(Assigned(AMemo));

  FClient:= TIdTCPClient.Create(nil);
  FClient.Host:=Host;
  FClient.Port:=Port;
  //copy references to member vars
  FMemo:= AMemo;
  //start thread
  Resume;
end;

procedure TMyThreadClass.Execute;
begin
  try
    FClient.Connect;
    while (not Terminated) and (FClient.Connected) do
    begin
      s:=FClient.Readln;
      Synchronize(UpdateMemo);
    end;
  finally
    FClient.Free;
  end;
end;

procedure TMyThreadClass.UpdateMemo;
begin
  //will be executed within the mainthread
  FMemo.Lines.Append(s);
end;

choose 22. Nov 2003 19:45

Re: Thread Anfängerfrage
 
Hallo Kamil,

es ist sicherlich eleganter (Geheinmisprinzip, Zuständigkeitsprinzip), wenn alles, was die Connection betrifft, nur innerhalb des Threads abgehandelt wird. Das ist auch der Gedanke meiner Bemerkung in den Klammern gewesen:
Zitat:

Zitat von choose
Der Thread bekommt die Referenzen auf die zu verwendenen Objekte übergeben und verwendet sie dort (besser noch: die Connection (FClient ist nur ihm bekannt)

Allerdings wollte ich zunächst auf das Problem der Sichtbarkeit aufmerksam machen und erklären, warum
Delphi-Quellcode:
client.ReadLn;
auch durch das Einbinden der Form-Unit nicht funktioniert...

Kamil 22. Nov 2003 20:00

Re: Thread Anfängerfrage
 
@choose: muss zugeben, dass ich deinen Beitrag nur schnell überflogen und die Klammer deshalb übersehen habe. Hab mich mehr auf den Quellcode konzentriert. Da der Titel "Thread Anfängerfrage" lautet dachte ich, dass sowas erwähnt werden sollte. Deshalb wird auch der Unterschied zwischen threadsicher und threadfähig wahrscheinlich noch nicht ganz klar sein und die Anweisund "if FClient.HasData then" im Thread kann ganz schnell zu größeren Problemen führen, weil sie höchst wahrscheinlich auch synchronisiert werden sollte.

Spurius 25. Nov 2003 15:57

Re: Thread Anfängerfrage
 
So, ich meld mich mal wieder.
ich hab jetzt die threadunit fertig, und habe sie unter uses eingebunden, wo auch windows, sysutils etc. stehen.
Mit
Code:
Self.FThread:= TMyThread.Create(Self.Client1, Self.Memo1);
kann ich aber keinen Thread erzeugen. da kommt dann unbekannt tmythread.
und wenn ich unter var, wo auch form : tform1 deklariert wird,
fthread: tmythread deklarieren will kommt auch dass tmythread unbekannt sind.
kann mir da jemand wieterhelfen?
Thx
Spurius

choose 25. Nov 2003 19:43

Re: Thread Anfängerfrage
 
Hallo Spurius,

löse das Problem so, wie Du dem unbekannten Typ TMemo und TidTCPClient innerhalb des Deklaration von TMyThread begegnet bist.

Spurius 25. Nov 2003 21:22

Re: Thread Anfängerfrage
 
da hab ich idtcpclient unter uses deklariert. mein thread heisst mythread, und die unit thread. soll ich da thread + mythread deklarieren oder wie?
ich blick da nicht so ganz durch, wär nett, wenn du mir weiterhelfen würdest.
Gruß
Spurius

choose 25. Nov 2003 23:27

Re: Thread Anfängerfrage
 
Such mal in der OH. Dort habe ich zB das gefunden
Zitat:

Zitat von OH
[...]A uses clause need include only units used directly by the program or unit in which the clause appears. That is, if unit A references constants, types, variables, procedures, or functions that are declared in unit B, then A must use B explicitly.[...]

Das Verwenden von Units ist unter Delphi ein elementares Prinzip und wird in dem D7 beiliegenden Buch "Sprachreferenz" gut beschrieben.

Spurius 26. Nov 2003 05:51

Re: Thread Anfängerfrage
 
Hallo,
ich hab das Buch nicht, kannst du mir nicht einfach sagen, wie das geht?
Thx
Spurius

choose 26. Nov 2003 08:25

Re: Thread Anfängerfrage
 
Hallo Spurius,

bitte verstehe mich nicht falsch: Ich helfe wirklich gerne.
Das Problem ist bei den gegenseitig verwendeten Units aber weniger das Wie für Deine konkrete Problemstellung als das Warum muss man bei der Verwendung von Typen, die in anderen Units deklariert sind, auf diese oder eine andere Weise reagieren.
Dies bedarf einer umfassenderen Erklärung statt einem kurzen Statement, mithilfe von drei Zeilen Code, die zudem, wenn an falscher Stelle platziert, unweigerlich vermeidbare Folgefragen nach sich ziehen würden.

Es handelt sich beim Einbinden von Units unter Pascal wie Delphi um ein grundlegendes Prinzip, das verstanden werden will und dessen Kenntnis von jeder Bilbiothek von Dritten (zB Komponenten-Suites) vorrausgesetzt wird. Ich halte es deshalb für sinnvoll, das dahinter stehende Prinzip zu erläutern.

Diese Thematik ist allerdings in der OH besser und umfassender beschrieben, als ich es hier könnte und sie bietet sich Dir als Informationsquelle an, die Du kurzfristig und zu jeder Zeit befragen kannst. Wenn Du also Dein Begleitmaterial zu Delphi verlegt hast, solltest Du sie stattdessen mit den Stichworten "unit-Klauseln" befragen. Dort findest Du auch Informationen zu dem von Dir entdeckten Prinzip der "zirkulären Unit-Referenzen" sowie dem Thema, dass Du zur Lösung Deines akuten Problem zu benötigen scheinst: "Mehrere und indirekte Unit-Referenzen", dem Abschnitt aus dem ich im letzten Posting zitiert habe und eigentlich schon die Lösung beschreibt.

Spurius 26. Nov 2003 12:48

Re: Thread Anfängerfrage
 
Hallo,
also erstmal Danke, dass du mir soviel hilfst.
Ich hab mir die uses-klauseln in der Hilfe angeschaut,(ich hab schülerversion, da war kein Handbuch dabei), bin aber nicht schlau daraus geworden, was das für mein Programm bedeutet. Ich habe bisher die Unit, in der der Thread geschrieben ist, unter den uses von meinem Hauptprogramm eingebunden.
Aber wie kann ich den darin beschriebenen Thread jetzt einsetzten? Ich versteh das nicht :(
Kannst du mir da vielleicht nochmal so ein Musterbeispiel machen, wie du es schonmal gemacht hast?
Gruß
Spurius

choose 26. Nov 2003 13:42

Re: Thread Anfängerfrage
 
Hallo Spurius,

wenn ich Deine Antwort richtig interpretiere, ist die Fehlermeldung "Undefinierter Bezeichner" inzwischen verschwunden und Du weißt nichts ganz, wie Du ein Exemplar Deiner Klasse erzeugen kannst.

Für ein ähnliches Szenario ist in der D7-OH unter TStringList dann "Verwendung von TStringList" und von dort "Eine neue Stringliste erstellen" beschrieben. Der Text handelt von einem zum Zeitpunkt der Formularerstellung erzeugten Objekt einer Klasse, die man aus dem OI nicht direkt kennt, zeigt die Benutzung zu einem späteren Zeitpunkt und auch, wie man das Objekt wieder freigibt.

Achtung: Treads-Objekte sollten vor der Freigabe unbedingt terminiert sein! Falls Du bei diesem Problem angelangt sein solltest, stelle die Frage am besten in einem neuen DP-Tread.

Spurius 26. Nov 2003 16:22

Re: Thread Anfängerfrage
 
Hallo,
ich hab mit das mit tstringlist angeschaut, und probiert das nachzumachen.
aber es ist ja nicht die thread-unit unbekannt, sondern mythread!
hier mal mein code
Delphi-Quellcode:
unit modul;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient,
  StdCtrls, ExtCtrls, Mask, ComCtrls, IdTCPServer,threadtest;  //  <- threadunit

type
  TForm1 = class(TForm)
    Label1: TLabel;
    PageControl1: TPageControl;
    TabSheet1: TTabSheet;
    Label3: TLabel;
    Label4: TLabel;
    Label5: TLabel;
    Label6: TLabel;
    Memo1: TMemo;
    connect: TButton;
    Button2: TButton;
    Edit1: TEdit;
    ip: TEdit;
    Port: TEdit;
    Edit2: TEdit;
    Button1: TButton;
    client: TIdTCPClient;
    Timer1: TTimer;
    server: TIdTCPServer;
    TabSheet2: TTabSheet;
    Label7: TLabel;
    Label8: TLabel;
    Label9: TLabel;
    Memo2: TMemo;
    serverport: TEdit;
    servername: TEdit;
    Button4: TButton;
    Button5: TButton;
    Memo3: TMemo;
    Button3: TButton;
    procedure connectClick(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure Button5Click(Sender: TObject);
    procedure serverConnect(AThread: TIdPeerThread);
    procedure serverExecute(AThread: TIdPeerThread);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);

  private
    { Private-Deklarationen }
  public
    fthread: tmythread;           //hier soll ein neuer thread erstelt werden, aber              
  end;                              mythread ist unbekannt!

var
  Form1: TForm1;

implementation
uses thread;
{$R *.dfm}

procedure TForm1.connectClick(Sender: TObject);
begin
 client.Port := strtoint(port.Text);
 client.Host := ip.Text;
 client.Connect;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin

 client.Writeln(edit2.Text + ': ' + edit1.text);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin

 memo1.Lines[0] := 'Chatprotokoll';
 memo2.lines[0] := 'Chatprotokoll';
 memo3.Lines[0] := 'Angemeldete Chatter';
end;
das ist der anfang. vielleicht kann ich damit mein problem verdeutlichen.
Gruß
Spurius

choose 26. Nov 2003 16:41

Re: Thread Anfängerfrage
 
Ist die Klasse tmythread im Interface-Teil der Unit threadtest deklariert?

[edit]Welchen Zweck hat die Unit thread, die Du in der Formular-Unit modul einbindest?[/edit]

Spurius 26. Nov 2003 17:13

Re: Thread Anfängerfrage
 
Hallo,
also ich hatte einen thread mythread und nicht Tmythread erstellt. der thread geht jetzt :)
aber er friert ein, ich denke das liegt daran, dass es client.hasdata nicht gibt.
Code:
unit threadtest;

interface

uses
  Classes,idtcpclient,dialogs,StdCtrls,sysutils;

type
  tmythread = class(TThread)
  private
    fclient:tidtcpclient;
    fmemo:tmemo;
    procedure updatememo;
  protected
    procedure Execute; override;
  public
   constructor create(const aclient: tidtcpclient;
   const amemo: tmemo);

  end;

implementation
uses modul;

constructor tmythread.create(const aclient: tidtcpclient;
         const amemo: tmemo);
begin
 inherited create(true);
 fclient := aclient;
 fmemo := amemo;
 resume;
end;

procedure tmythread.Execute;
begin
 while not terminated do
   if fclient. then     //<- hasdata ???
   synchronize(updatememo)
  else
   sleep(100);
end;


procedure tmythread.updatememo;
begin

 fmemo.Lines.Append(fclient.ReadLn);
end;
end.
Das ist meine Threadunit. und da der thread sonst immer ausgeführt wird, ist es ja klar, dass das prog einfriert. weisst du, was ich da hinter client einsetzten könnte?
Gruß
Spurius

Kamil 26. Nov 2003 17:24

Re: Thread Anfängerfrage
 
Schau dir nochmal das was ich geschrieben habe an. Das sollte dir helfen.

Spurius 26. Nov 2003 17:52

Re: Thread Anfängerfrage
 
Jetzt hab ich aber mit viel Mühe den Thread hinbekommen, und würde den gern weiterverwenden.
Trotzdem Danke
Spurius

Kamil 26. Nov 2003 17:58

Re: Thread Anfängerfrage
 
Wenn du deinen Code nicht ändern wirst, dann wird dein Problem natürlich weiterhin bestehen... du muss schon was ändern und soviel ist das auch nicht.

Spurius 26. Nov 2003 18:05

Re: Thread Anfängerfrage
 
Ok, wenns nicht anders geht, soll ich einfach den code nehmen, den du schonmal gepostet hast?
und was ist da der Unterschied zu meinem? Ich mein, warum sollte das nicht einfrieren?
Gruß
Spurius

choose 26. Nov 2003 18:16

Re: Thread Anfängerfrage
 
Spurius, der von mir gepostete Code war bewusst exemplarisch und kann nicht funktionieren, weil es die Eigenschaft HasData in der Klasse TidTCPClient nicht gibt!
Was Du als "einfrieren" beschreibst, sollte eigentlich ein Abbruch des Kompilerlaufs sein, der Dich auf diese Tatsache aufmerksam macht. In der OH findest Du eine Übersicht der zur Verfügung stehenden Eigenschaften und in einem Buch bestimmt auch Lösungen inkl. Beispiele für viele Grundsatzfragen zu Pascal/Delphi...

Kamil 26. Nov 2003 18:27

Re: Thread Anfängerfrage
 
Der Unterschied liegt vorallem an UpdateMemo, das über Synchronize aufgerufen wird.
In dieser Funktion sollten keine blockierenden Aufrufe (wie z.B. TIdTCPClient.Readln, Sleep) stehen, da der Code nicht im Thread ausgeführt wird zu dem er gehört, sondern im Hauptthread deiner Anwendung, was natürlich deine Anwendung dann einfriert.

Vielelicht hilft dir das ein wenig:
Code:
 läuft im                   läuft im
Hauptthread                 MyThread
     |
     |
     |---->TMyThread.Create---->|
     |                          |
     |                          |
     |                          |
     |                          |
     | |<---Synchronize<--------|
     | |
     | |
     | | Ende von Sync. ------->|
     |                          |
     |                          |
     |                          |
     |                          |
     V                         V
Schau dir mal dieses Tutorial an:
http://www.tutorials.delphi-source.de/threads/

Spurius 26. Nov 2003 18:57

Re: Thread Anfängerfrage
 
Hallo,
könnt ihr mir nicht einfach sagen, wie ich das machen soll?
Wo liegt denn mein Fehler?
Im mainthread oder in mythread?
Gruß
Spurius

Kamil 26. Nov 2003 20:20

Re: Thread Anfängerfrage
 
Wie sieht dein Code vom Thread gerade aus?
Wahrscheinlich ist der Fehler, dass der Code aus dem Thread im Hauptthread ausgeführt wird.
Oder hast du es schon so gemacht wie ich es gepostet habe?

Spurius 27. Nov 2003 12:49

Re: Thread Anfängerfrage
 
Hallo,
das ist meinThreadcode:
Delphi-Quellcode:
unit threadtest;

interface

uses
  Classes,idtcpclient,dialogs,StdCtrls,sysutils;

type
  tmythread = class(TThread)
  private
    fclient:tidtcpclient;
    fmemo:tmemo;
    procedure updatememo;
  protected
    procedure Execute; override;
  public
   constructor create(const aclient: tidtcpclient;
   const amemo: tmemo);

  end;

implementation
uses modul;

constructor tmythread.create(const aclient: tidtcpclient;
         const amemo: tmemo);
begin
 inherited create(true);
 fclient := aclient;
 fmemo := amemo;
 resume;
end;

procedure tmythread.Execute;
begin
 while not terminated do
   if fclient. then     //<- hasdata ???
   synchronize(updatememo)
  else
   sleep(100);
end;


procedure tmythread.updatememo;
begin

 fmemo.Lines.Append(fclient.ReadLn);
end;
end.
Was ist in deinem Code wesentlich anders? Oder was soll ich ändern?
Gruß
Spurius

choose 27. Nov 2003 13:02

Re: Thread Anfängerfrage
 
Das Problem ist, wie Kamil bereits beschrieben hat, dass ReadLn geblockt ist, also die "Programmzeile so lange wartet bis tatsächlich ein String ankommt". Mit Syncronize wechselst Du in den Haupthread, so dass die Idee des Threads, nämlich diese Wartezeit unabhäng vom Hauptthread zu verbringen, untergraben wird...
Bei meiner exemplarischen Darstellung habe ich diesen Effekt durch eine fiktive Methode/Eigenschaft HasData kompensiert, die solange False zurückgibt, bis tatsächlich Daten vorhanden sind, so dass ReadLn niemals warten würde.
Die Schleife
Delphi-Quellcode:
while not Terminated do
  if FClient.HasData then
    Synchronize(UpdateMemo)
  else
    Sleep(100);
entspricht demnach im Wesentlichen einem Polling (tatsächlich nicht wirklich elegant).
Um das Ganze in den Griff zu bekommen, solltest Du innerhalb von Execute eine Exemplar-Variable mit dem Ergebnis von ReadLn füllen und erst anschließend Synchronize aufrufen, also in etwa so
Delphi-Quellcode:
while not Terminated do
begin
  FReceivedString:= FClient.ReadLn;
  Synchronize(UpdateMemo);
end;
Beachte, dass Du innerhalb von UpdateMemo diese Exemplarvariable anstatt FClient verwendest!

Nachteil dieses Ansatzes ist die Tatsache, dass ReadLn per default "unendlich lange" wartet, Du also keine saubere Möglichkeit hast, den Thread zu beenden. Hier könnte ggf doch eine Variante des Pollings verwendet werden, damit zyklisch Terminated abgefragt wird...

Spurius 27. Nov 2003 13:37

Re: Thread Anfängerfrage
 
Hallo,
ich hab nochmal deinen Code hier:
Delphi-Quellcode:
Source:
type
  TMyThreadClass = class(TThread)
  private
    FClient : TSomeClient;
    FMemo : TSomeVCLControl;
    procedure UpdateMemo;
  protected
    s: string;
    procedure Execute;override;
  public
    constructor Create(Host: string; Port: integer; const AMemo: TSomeVCLControl);
  end;


constructor TMyThreadClass.Create(Host: string; Port: integer; const AMemo: TSomeVCLControl);
begin
  inherited Create(True);
  Assert(Assigned(AMemo));

  FClient:= TIdTCPClient.Create(nil);
  FClient.Host:=Host;
  FClient.Port:=Port;
  //copy references to member vars
  FMemo:= AMemo;
  //start thread
  Resume;
end;

procedure TMyThreadClass.Execute;
begin
  try
    FClient.Connect;
    while (not Terminated) and (FClient.Connected) do
    begin
      s:=FClient.Readln;
      Synchronize(UpdateMemo);
    end;
  finally
    FClient.Free;
  end;
end;

procedure TMyThreadClass.UpdateMemo;
begin
  //will be executed within the mainthread
  FMemo.Lines.Append(s);
end;
hier wird ja ein client created, was hat der mit meinem tatsächlichen client im mainthread zu tun?
und wenn ich den create, muss ich ja auch dafür port und host festlegen. aber die werden ja im mainthread über edits festgelegt. wie bekomm ich da werte?
Gruß
Spurius

Kamil 27. Nov 2003 14:56

Re: Thread Anfängerfrage
 
Zitat:

hier wird ja ein client created, was hat der mit meinem tatsächlichen client im mainthread zu tun?
natürlich nichts!
Zitat:

und wenn ich den create, muss ich ja auch dafür port und host festlegen. aber die werden ja im mainthread über edits festgelegt. wie bekomm ich da werte?
die Werte übergibst du mit Hilfe der Parameter von dem Konstruktor.

Zitat:

Nachteil dieses Ansatzes ist die Tatsache, dass ReadLn per default "unendlich lange" wartet, Du also keine saubere Möglichkeit hast, den Thread zu beenden. Hier könnte ggf doch eine Variante des Pollings verwendet werden, damit zyklisch Terminated abgefragt wird...
Das solltest du nicht unbeachtet lassen!!! Schau dir die Parameter von ReadLn an!

choose 27. Nov 2003 15:14

Re: Thread Anfängerfrage
 
Zitat:

Zitat von Kamil
Zitat:

Zitat von choose
Hier könnte ggf doch eine Variante des Pollings verwendet werden, damit zyklisch Terminated abgefragt wird...

Das solltest du nicht unbeachtet lassen!!! Schau dir die Parameter von ReadLn an!

Hallo Kamil,

könntest Du kurz skizzieren, wie Du ohne Polling (sprich: ATimeOut<>IdTimeoutInfinite) den Thread sauber terminierst (if Self.Terminated) und trotzdem jedes Byte von FClient empfängst?


Alle Zeitangaben in WEZ +1. Es ist jetzt 05:57 Uhr.
Seite 1 von 2  1 2      

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz