Autor Beitrag
reimo
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 75



BeitragVerfasst: Di 02.03.04 09:27 
Hi,

also folgendes, ich möchte in einem (oder mehreren) von mir während der Laufzeit erzeugten threads eine Instanz einer abgeleiteten Klasse von TForm erzeugen. Der Thread soll solange bestehen bleiben, bis die Form geschlossen wird, ich hab aber keine Ahnung wie ich so etwas implementieren soll, da die Form im neuen Thread auf unterschiedliche Ereignisse reagieren soll ( IP-Connections, Button Clicks,..).

Wie soll die Thread-Funktion ausschauen, damit es funktioniert?
Hab folgendes versucht (schon mit der Gewissheit, dass es nicht funktionieren wird)

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
function ThreadFkf( x:Pointer ): ....
   var Form2: TFormThread;
begin
   Form2 := TFormThread.Create(...);

   while TRUE do begin end;   // kann ja nicht funktionieren, da das System durch die while-schleife blockiert wird
                                          // aber, was muss ich machen, damit die Form/der Thread bis zum schliessen der Form bestehen bleiben
                                          // und auf die unterschiedlichen Ereignisse reagieren können

   Form2.Free;
end;



mein zweiter Ansatz war, es über Aplication zu versuchen und es funktioniert, aber ich bin mir nicht sicher, ob es auch korrekt ist:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
function TMainForm.ButtonClick.....
begin

  Application.Initialize;                                                
  Application.CreateForm(TFormThread, Form2);
  Application.Run;

end;


kann ich einfach so die Instanz Application verwenden, oder muss ich meine eigene Instanz von TApplication erzeugen?
Worauf sollte ich bei so einer Erzeugung eines Threads aufpassen?

Falls die zweite Syntax richtig ist, würde mich aber trotzdem interessieren, auf welche weise ich in meinem eigenen Thread eine Form erzeuge?

danke im voraus
reimo

PS.:
Ich weiss, dass ich in der letzten Zeit ziemlich viele Fragen stelle, da ich aber eh schon auf der Suche nach einem ordentlichen Delphi-Buch bin, wird sich dies hofentlich bald einstellen.
Bin leider gezwungen in Delphi 4 zu programmieren, und da noch ein Buch zu finden ist nicht ganz so einfach
Die Delphi-Tutorials von Luckie kenn ich schon
KidPaddle
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 351

WinXP,Linux
D7 Prof, XE7
BeitragVerfasst: Di 02.03.04 10:18 
Hallo,

in der Hilfe zu Threads wird darauf hingewiesen, daß alle Nachrichten nur im Kontext des Hauptthread, z. B. nach einem Synchronize - Aufruf, verarbeitete werden sollten. Ich denke dieser Lösungsansatz von Dir wird daher nicht funktionieren.

Gruß
KidPaddle
Loryn
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 34

Win 98, ME
D4 Stand.
BeitragVerfasst: Di 02.03.04 11:18 
Hallo,

wenn es "nur" darum geht, die Lebensdauer des Threads an die Lebensdauer der Form zu knüpfen, könnte man die Form TThreadForm im Haupt-Thread erzeugen und im OnClose-Ereignis der Form den Thread beenden, indem man TThread.Terminate aufruft.

Wäre das eine Lösung?

Gruß Loryn
reimo Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 75



BeitragVerfasst: Di 02.03.04 11:24 
@KidPaddle

auf welchen Ansatz von mir beziehst du dich?

durch die Verwendung von:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
function TMainForm.ButtonClick..... 
begin 

  Application.Initialize;                                                 
  Application.CreateForm(TFormThread, Form2); 
  Application.Run; 

end;


funktioniert es ja (es wird ein neuer Thread erzeugt), ich weiss aber nicht ob es zulässig ist, was ich da mache (siehe mein erster Post) ?

Und was den ersten Ansatz angeht, glaube ich nicht, dass es nicht funktioniert, über Application wird ja auch ein SubThread erzeugt, der Nachrichten empfangen kann, ob diese dann vom HauptThread weitergereicht werden, oder er diese direkt empfängt ist mir eigentlich egal, hauptsache ist, dass ich eine Form in einem Subthread hab, und ich würde gerne wissen, wie ich soetwas anstelle!
reimo Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 75



BeitragVerfasst: Di 02.03.04 11:45 
@Loryn
danke für den Vorschlag, die Klasse TThreadForm kenn ich noch garnicht, werd sie mir aber gleich anschauen.

wie sieht es aber aus, wenn es nicht um eine Form geht, sondern um eine andere VCL-Komponente:
zB.: Ich leite eine eigene Klasse von TClientSocket ab, oder Kapsle diese nur
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
IPConnection = Class
IP: TClientSocket
xy: Integer;
..
..
..
end;


function ThreadFunction( p: Pointer )....
   var IP: IPConnection;
begin
   IP := IPConnection.Create;
   
   IP.IP.Active := TRUE;
   // so was mach ich jetzt? gibt es eine Möglichkeit den thread auf bestimmte Ereignisse warten zu lassen?
   // die Verbindung soll ja dauernd bestehen bleiben, und nicht gleich wieder terminiert werden, also
   // eine Schleife
   while ...
      // ????????????
   end;// while

end;


mfg
reimo
Loryn
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 34

Win 98, ME
D4 Stand.
BeitragVerfasst: Di 02.03.04 12:13 
@Reimo

Ich habe mich verschrieben, ich meinte mit TThreadForm deine Angabe TFormThread!

Schau doch mal in der Hilfe das Beispiel zu TThread.Terminated an. Dort ist ein Beispiel für TThread.Execute (mit while-Schleife).

Mir ist nicht ganz klar, welche Aufgabe dein Sub-Thread übernehmen soll und wozu der dringend eine eigene Form braucht? Was meinst du mit ThreadFunction? Soll das TThread.Execute sein?

Gruß Loryn
reimo Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 75



BeitragVerfasst: Di 02.03.04 13:13 
Zitat:
Was meinst du mit ThreadFunction? Soll das TThread.Execute sein?


Ja, im Falle der Klasse TThread wäre das TThread.Execute, es gibt aber noch andere mögkichkeiten einen Thread zu erzeugen zB.: CreateThread(...). Dieser Funktion übergibst du als Parameter den Pointer auf die Thread-Funktion.

Zum Programm:
Also, der Thread soll ein IP-Interface darstellen, d.h. empfangen/verschicken von Daten. Die empfangenen Daten wollte ich in einem Memo-Feld darstellen.
Die Sache mit der Form ist eigentlich sekundär, es geht mir im allgemeinen um das Epfangen von Nachrichten (OnConnect,...) im Subthread, aber anscheinend ist dies ja nicht so einfach.
Ich werde es, wie schon KidPaddle gepostet hat, mal mit Synchronize versuchen, was mir aber nicht gefällt:
es werden alle Daten, die eigentlich nur der SubThread zum Arbeiten benötigt erst über den Hauptthread geschickt -> unnötig

HauptThread.Socket1OnConnect
--> Synchronize( OnConnect ) // unnötige Unterbrechung Hauptthreads

-->Subthread.OnConnect
begin
Haupthread.Socket.ReadBuffer(...) // Und dann noch die direkten Zugriffe auf die Members des Hauptthreads !
end

Vielleicht hab ich das Synchronize-Prinzip falsch verstanden, und ich erzähle nur Mist, also bitte korrigiert mich, falls dem so ist.

@Hilfe-Beispiel bei TThread
Hier wird leider nur das Versenden über ClientSocket gezeigt, aber leider nicht das Empfangen -> OnRead event

Wie wird es eigentlich über die Klasse Application.Run gemacht? hier scheint ja das ganze zu funktionieren

mfg
reimo
Loryn
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 34

Win 98, ME
D4 Stand.
BeitragVerfasst: Di 02.03.04 16:17 
@Applikation.Run:

In deiner Projektdatei (Menü Projekt/Quelltext anzeigen) wird so etwas stehen wie:

ausblenden Delphi-Quelltext
1:
2:
3:
  Application.Initialize;
  Application.CreateForm(TTestForm, TestForm);
  Application.Run;


Application.Run arbeitet sozusagen in einer Endlosschleife alle Windows-Botschaften ab, die für deine Anwendung bestimmt sind. Als Reaktion dieser Botschaften wird z.B. TMainForm.ButtonClick aufgerufen.

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
function TMainForm.ButtonClick.....  
begin  
  Application.Initialize;                                                  
  Application.CreateForm(TFormThread, Form2);  
  Application.Run;  
end;


Wenn du jetzt in TMainForm.ButtonClick erneut Application.Run aufrufst, wird die Botschaftswarteschlange dort abgewickelt, aber es entsteht kein neuer Thread dadurch. Gewissermassen passiert gar nichts Neues, mit der Ausnahme, dass alle Anweisungen, die nach TMainForm.ButtonClick kommen sollten erst abgearbeitet werden können, wenn Application.Run beendet wird; und das ist bei Anwendungsende.

@Empfang von Nachrichten:
Leider kenne ich TClientSocket nicht. Ich verfüge nur über Delphi4 Standard. Ist es richtig, dass bei Empfang einer Nachricht ein OnRead-Event ausgelöst wird? Dann könnte es folgendermassen gehen:
In der while-Schleife deiner Thread-Funktion (z.B. TThread.Execute) fragst du ab, ob Nachrichten da sind. Wenn nicht legst du dich schlafen (z.B. TThread.Suspend). Im TClientSocket.OnRead-Event weckst du den Thread wieder auf (z.B. TThread.Resume). Du landest wieder in der while-Schleife und kannst die anliegende Nachrichte verarbeiten.

@Synchronize:
Ich habe das so verstanden, dass mit Synchronize der konkurrierende Zugriff mehrerer Threads auf ein Objekt vermieden werden soll. Wenn jetzt aber der TClientSocket nur vom Subthread verwendet wird, wieso sollte man Synchronize verwenden und die ganze Abarbeitung über den Hauptthread schicken?

Vielleicht weiss jemand mehr darüber? (Ich lerne gern etwas dazu!)

Gruß Loryn
KidPaddle
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 351

WinXP,Linux
D7 Prof, XE7
BeitragVerfasst: Di 02.03.04 17:38 
Ich habe mir für ein Projekt ein SQLMonitor gebastelt, welcher zentral auf einem Server lief. Die SQL - Anweisungen wurden über UDP von den Clients (immerhing an die 40) an den Server geschickt und in einer Memo - Box anzeigt. Verwendet habe ich dafür die Indy - Komponenten, was aber kein Unterschied machen sollte. Die Indy - Komponenten arbeiten mit Threads.

Die ankommenden Daten wurden in einer TStringliste zwischen gespeichert und nicht sofort angezeigt.
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
var
  DataStringStream : TStringStream;
begin
  fLocked := True;
  try
    DataStringStream := TStringStream.Create('');
    try
      DataStringStream.CopyFrom(AData, AData.Size);
      EnterCriticalSection(fLock);
      try
        fReceivedData.Add(FormatDateTime('hh:nn:ss:zzz', Now) + ' - ' + ABinding.PeerIP + ' - ' + DataStringStream.DataString);
      finally
        LeaveCriticalSection(fLock);
      end;
    finally
      DataStringStream.Free;
    end;
  finally
    fLocked := False;
  end;


Wichtig hierbei sind die EnterCriticalSection und LeaveCriticalSection - Aufrufe, die verhindern, das der Hauptthread auf die Variable fReceivedData zu greift. Damit die Daten auch in der Memobox angezeigt werden, nahm ich einen Timer, der regelmäßig die Daten aus fReceivedData an die vorhanden Memo - Einträge anfügt.

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
procedure TMainForm.timerUpdateTimer(Sender: TObject);
var
  TempList         : TStringlist;
begin
  if (fReceivedData.Count > 0then begin
    // Es wurden neue SQL - Befehle abgefangen, die noch nicht in die
    // Memo - Komponente uebernommen wurden.
    TempList := TStringlist.Create;
    try
      EnterCriticalSection(fLock);
      try
        if fLocked then Exit;
        TempList.Assign(fReceivedData);
        fReceivedData.Clear;
      finally
        LeaveCriticalSection(fLock);
      end;
      memoSQLMonitor.Lines.AddStrings(TempList);
    finally
      FreeAndNil(TempList);
    end;
  end;
end;


Auch hier wird das umkopieren der Daten mit einer EnterCriticalSection/LeaveCriticalSection geschützt, damit nicht während der Zeit für das kopieren neue Daten über die UDP - Schnittstelle angehängt werden.

Das funktioniert sehr gut bei ca. 40 Clients parallel und mit ca. 5-7 neue SQL - Anweisungen pro Sekunde. Da ich nur Objekte verwendet habe, die in die Nachrichtenverarbeitung eingreift, braucht ich mir keine Gedanken über Synchronize machen.

Gruß
KidPaddle
reimo Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 75



BeitragVerfasst: Di 02.03.04 17:39 
ausblenden Delphi-Quelltext
1:
In der while-Schleife deiner Thread-Funktion (z.B. TThread.Execute) fragst du ab, ob Nachrichten da sind.					


an so eine Lösung hab ich auch schon gedacht, leider weiss ich nicht wie das Abfragen/verschicken von Nachrichten an/von threads funktioniert. Wäre für jeden Tip dankbar ( schaue mir gerade TEvent an )
Leider find ich diese Lösung auch nicht wirklich sauber, da TClientSocket immer noch ein Member der Hauptform ist, obwohl diese nichts damit anzufangen weiss. Es werden alle Messages/Events über die Mainform an den SubThread geliefert, was ich vermeiden wollte.

TMainForm.ClientSocket.OnRead
begin
// Dazugehörigen Thread aufwecken
// TEvent -> Nachricht schicken, dass Daten zu empfangen sind
end;

Da dieser Thread ein selbständiger Agent sein soll, und je nach Bedarf beliebig viele solcher Agents erzeugt werden können ist der Ansatz der Socketkomponente in der Mainform nicht wirklich das richtige.
Es sollte einfach das Erzeugen einer Instanz reichen und man bräuchte keinen zusätzlichen Code in der Mainform.
zB.:
// Mainform
....
Agent[..] := TAgent.Create( Socket, IP );
Agen.Run;

Um den Rest braucht sich die Mainform nicht kümmern ( im Bezug auf die Schnittstellenanbindung )

@Aplication.Run
Hast recht, jetzt hab ichs erneut ausprobiert, und es funktioniert nicht mehr, keine Ahnung, was ich zuvor für'n Mist baut hab.
Wieso wird aber beim erzeugen einer Form aus der Hauptform ein neuer Thread erzeugt.

SubForm := TSubForm.Create; // neuer Thread laut taskmanager
SubForm2 := TSubForm.Create; // kein neuer Thread mehr ???

wird aber noch eine Form aus der Huptform erzeugt, so ändert sich an der Threadzahl nichts mehr. ???

mfg
reimo
reimo Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 75



BeitragVerfasst: Di 02.03.04 18:08 
@KidPaddle
Ich scheitere aber bereits an der Eventbehandlung(OnRead,..) von einem SubThread aus unter der Verwendung der VCL-Komponenten.
Das heisst, nicht die MainForm soll auf die IP-Events reagieren, sondern der SubThread und die Mainform soll dabei für Usereingaben nicht blockiert werden.
KidPaddle
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 351

WinXP,Linux
D7 Prof, XE7
BeitragVerfasst: Di 02.03.04 18:46 
Kannst Du mir das mal zusenden wie Du es umgesetzt hast? Ich schaue es mir gerne an.

Gruß
KidPaddle
reimo Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 75



BeitragVerfasst: Di 02.03.04 19:27 
würd ich gerne machen, hab aber leider nichts richtiges, nach jedem gescheiterten Versuch wird ein neues Projekt angelegt, und auf ein neues. Die Versuche kannst aber haben (erst morgen, muss jetzt leider weg)
Kurz zur erklärung (hab ich aber glub ich schon gepostet)

erzeuge Form1 -> mainform

Form1.Buttonclick -> erzeugt einen Thread
-> in diesem Thread soll eine Klasse erzeugt werden, die TClientSocket beinhaltet (zB Form2*)
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
function  threadfunction
begin
   Form2 := TForm2.create;
   Form2.Socket.Active := TRUE;
   // was nun? ich möchte den Thread nicht beenden, will auf daten vom Server warten (OnRead - Event ???)
   while XYZ do begin
      if Socket.SocketReceiveLen > 0 then begin           // keine ahnung obs funktioniert, hab ich noch nicht ausprobiert
          Socket.SocketRecieveBuffer(Buffer....);           // aber da könnte ich es gleich über WinSock machen
          Memo1.Lines.Add( Buffer );
      end
      else Sleep(100)
   end;
end;

-> und jetzt baust eine Verbindung zu einem Server auf (von der Klasse, die im SubThread erzeugt wurde), dieser sendet zum Beispiel Zustandsdaten
-> gibst die Daten in Form2 aus
-> der Client läuft, bis die Application beendet wird.

*)
ausblenden Delphi-Quelltext
1:
2:
3:
Form2 = class(TForm)
..
   Socket: TClientSocket


sers,
reimo
Motzi
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2931

XP Prof, Vista Business
D6, D2k5-D2k7 je Prof
BeitragVerfasst: Di 02.03.04 20:04 
Die VCL ist nicht threadsafe und es funktioniert auch nicht eine Form komplett in einem anderen Thread laufen zu lassen..! Ich hab mal vor längerer Zeit eine ähnliche Frage im Entwickler-Forum gestellt... hier ist der Link

_________________
gringo pussy cats - eef i see you i will pull your tail out by eets roots!
Loryn
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 34

Win 98, ME
D4 Stand.
BeitragVerfasst: Di 02.03.04 23:20 
@Motzi
Ich habe in der Delphi-Hilfe über Threads nachgelesen und bin auch deinem Link nachgegangen. Soweit habe ich alles verstanden.

Der Vollständigkeit halber eine abschliessende Frage: Welche Delphi-Klassen sind threadsicher? Alle Klassen, die nicht zur VCL gehören? Woran ist das zu erkennen? Z.B. alles was von TControl abstammt ist nicht threadsicher, der Rest aber doch?

Gruß Loryn
reimo Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 75



BeitragVerfasst: Mi 03.03.04 10:43 
Hi,

Danke für die Antworten, werde wohl die IP-Connection selber schreiben müssen und auf die Form/VCL verzichten.

mfg
reimo
Motzi
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2931

XP Prof, Vista Business
D6, D2k5-D2k7 je Prof
BeitragVerfasst: Mi 03.03.04 14:49 
Loryn hat folgendes geschrieben:
Der Vollständigkeit halber eine abschliessende Frage: Welche Delphi-Klassen sind threadsicher? Alle Klassen, die nicht zur VCL gehören? Woran ist das zu erkennen? Z.B. alles was von TControl abstammt ist nicht threadsicher, der Rest aber doch?

Das kann man so nicht beantworten... ich weiß jetzt nicht so ganz genau welche VCL-Klassen jetzt intern wie funktionieren.. auf auf jeden Fall kann man mal sagen, dass alle GUI-Controls mit denen man sich im Form-Designer so schön herumspielen kann nicht threadsafe sind und nachdem der "kleinste gemeinsame Nenner" TComponent ist würd ich mal sagen bei allen Nachfahren TComponent muss man aufpassen... will das aber hier nicht zu verallgemeinern und es kommt teilweise auch sicher darauf an wie man das ganze einsetzt...

_________________
gringo pussy cats - eef i see you i will pull your tail out by eets roots!