Autor Beitrag
funcry
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 110
Erhaltene Danke: 1

Win7 64, XP 32
C# (VS 2010 EE), Delphi (TD 2006 Win32)
BeitragVerfasst: Sa 06.12.08 19:05 
Hallo zusammen,

ich arbeite derzeit an einem privaten Projekt, und verstehe nicht wie man _sauber_ folgende Struktur hinbekommen kann. das Problem ist wie folgt:

Es gibt eine Basisklasse:

ausblenden Delphi-Quelltext
1:
Tmessage = class(TObject)					


welche nichts anders tut als Meldungen auf ein im übergebenes TRichEdit auszugeben.

Davon abgeleitet gibt es eine Klasse:

ausblenden Delphi-Quelltext
1:
rechnen = class(TMessage)					


welche bestimmte Rechenoperationen durchführt.

Innerhalt der Klasse "rechnen" gibt es eine funktion:

ausblenden Delphi-Quelltext
1:
function vielrechnen(param1, param2: double):Boolean;					


welche eine sehr lange Berechnung durchführt.

Problem:
Meine Überlegung ist, dass ich gerne die Klasse "rechnen" mehrfach erstelle würde und die dort enthaltene Funktion "vielrechnen" jeweils in einem eigenen Thread ablaufen lassen würde. Das ganze liegt natürlich im Quelltext vor, welcher jedoch rund 1000 Zeilen hat, daher habe ich das Problem hier abstrahiert. Kurz gesagt, mein Problem ist, wie überführe ich mein Objekt "rechnen" sauber in einen Thread.

Getestet habe ich folgende Varianten, finde diese jedoch alle unsauber.

Variante 1:
Einführen eine Basisklasse:
ausblenden Delphi-Quelltext
1:
Tsuperthread = class(TThread)					

davon abgeleitet
ausblenden Delphi-Quelltext
1:
Tmessage = class(Tsuperthread)					

davon abgeleitet
ausblenden Delphi-Quelltext
1:
Trechnen = class(Tmessage)					


(Innerhalb von Tmessage kann ich execute von tthread überschreiben, und dort vielrechnen aufrufen)
Prinzipiell würde das klappen, nur wäre das nicht mit Kanonen auf Spatzen geschossen ? Ich will ja eigentlich nur die Funktion vielrechnen in "rechnen" in einem eigen Thread ausführen!

Variante 2:
Trechnen ist abgeleitet von TThread, und enthält als lokale Variabble:
ausblenden Delphi-Quelltext
1:
var message: TMessage;					


Würde auch funktionieren, wobei ich ungern auf die Verebung an dieser Stelle verzichten will.

Variante 3:
TRechnen bekommt als nested Objekt einen eigenen thread:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
trechnen = class(Tmessage)
type TmyThread = class(TThread)
[...]
end;
[...]
end;


Nur wie greife ich in TmyThread auf Funktionen von TRechnen zu ?

Irgendwie klappt das nicht.

Variante 4:
Ich erstelle eine Klasse:
ausblenden Delphi-Quelltext
1:
ThreadedRechnen = class(TThread, TRechnen)					


So sollte es eigentlich funktionieren, nur kann das Delphi nicht wie mir scheint.


Daher die Frage an Euch: Was ist eine saubere Lösung für das beschriebene Problem ?
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19315
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Sa 06.12.08 19:20 
Du könntest mit BeginThread einen Thread erzeugen, der nur aus der Funktion besteht. Die verlinkte Seite enthält Informationen zur durch diese Delphifunktion gekapselten API-Funktion.
Mehr dazu findest du u.a. hier:
www.delphi-treff.de/...scal/threads/page/5/
und hier:
www.delphipraxis.net/post684623.html

Ansonsten kannst du auch Variante 3 benutzen. Der Thread müsste ggf. eine Referenz auf die anderen Klassen bekommen und Zugriffe darauf mit Synchronize synchronisieren, d.h. im Kontext des Hauptthreads ausführen, insbesondere bei VCL-Zugriffen gibt es sonst Probleme.
funcry Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 110
Erhaltene Danke: 1

Win7 64, XP 32
C# (VS 2010 EE), Delphi (TD 2006 Win32)
BeitragVerfasst: Sa 06.12.08 23:43 
Danke für die prompte Antwort! Das ist die Lösung.

Innerhalb meiner Klasse "rechnen" habe ich folgende Methode eingebaut, welche als Thread die ursprüngliche Methode "vielrechnen" aufruft. Natürlich ist das nur das Grundgerüst (ohne Exceptions, Endthread etc. ...), aber der Weg scheint mir praktikabel - und einigermassen übersichtlich - zu sein :-)

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
procedure Trechnen.vielrechnen_asThread;
  function Test(P: Pointer): Integer;
  var
    My_rechnen: Trechnen;
  begin
    My_rechnen := p;
    My_rechnen.vielrechnen;  // Aufruf der tatsächlichen Funktion.
  end;

var
  ThreadID: LongWord;

begin
  BeginThread(nil0, Addr(Test), self, 0, ThreadID);
end;
funcry Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 110
Erhaltene Danke: 1

Win7 64, XP 32
C# (VS 2010 EE), Delphi (TD 2006 Win32)
BeitragVerfasst: Sa 13.12.08 10:41 
Ich muss nochmal darauf zurückkommen. Zwischenzeitlich habe ich Variante 4 umgesetzt, weil ich über die Frage gestolpert bin, wann kann ich einen Thread wieder freigeben. Allerdings habe ich auch hier ekien Lösung gefunden, so dass ich jetzt wieder auf die hier vorgeschlagene Variante zurückgekommen bin.

Sowohl (!!!) bei variante 4 als auch hier verstehe ich eines nicht:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
procedure Trechnen.vielrechnen_asThread;
  function Test(P: Pointer): Integer;
  var
    My_rechnen: Trechnen;
  begin
    My_rechnen := p;
    My_rechnen.vielrechnen;  // Aufruf der tatsächlichen Funktion.
  end;

var
  ThreadID: LongWord;

begin
  BeginThread(nil0, Addr(Test), self, 0, ThreadID);
end;


Dies habe ich letztlich in dies überführt:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
procedure Trechnen.vielrechnen_asThread;
  function Test(P: Pointer): Integer;
    self.vielrechnen(p);  // Aufruf der tatsächlichen Funktion.
  end;

var
  ThreadID: LongWord;

begin
  BeginThread(nil0, Addr(Test), self, 0, ThreadID);
end;


mit:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
Trechnen.vielrechnen(sender: Trechnen);
begin
  self := sender;  // ?? warum ??
  [...]
end;


Die Zeile:

ausblenden Delphi-Quelltext
1:
self := sender					


ist dabei notwendig, da - so sieht es im Debugger aus - vielrechnen nicht auf Felder innerhalb des eigenen Objektes zugreifen kann. Spricht diese sind entweder NIL oder 0. Und das obwohl ich ja vielrechnen aus dem eigenen Objekt heraus aufgerufen habe. Genau die gleiche Problematik war auch bei Variante 4. Wobei - um die Verwirrung komplett zu machen - bei Methoden welche ich in vielrechnen aufrufe, welcher sich widerum auf Objektfelder des eigenen Objektes beziehen, dieses Problem nicht bestand.

Woran liegt das ? Und ist das wirklich sauber programmiert ?