Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Delphi Parameterübergabe bei BeginThread für Klassenmethode (https://www.delphipraxis.net/152688-parameteruebergabe-bei-beginthread-fuer-klassenmethode.html)

s.h.a.r.k 2. Jul 2010 01:34

Parameterübergabe bei BeginThread für Klassenmethode
 
Ich habe im Moment eine threoretische Fragen, bei der ich nicht ganz verstehe, warum es denn nicht klappt. Und zwar kann ich via
Delphi-Quellcode:
BeginThread()
ja den Code einer Procedure als Thread ausführen -- das ganze klappt auch wunderbar mit einer Klassenmethode. Nun wollte ich auf Self als Parameter übergeben, was ich auch via [delphi]Pointer(Self)[/delphi) getan habe.

Wenn ich nun eine Klassenmethode via BeginThread ausführe, so bekomme ich es nicht gebacken, Self über den Parameter zu erhalten. Habe ich statt dessen eine (globale) Procedure als Thread-Code, so funktioniert die Übergabe von Self ohne Probleme. Nur warum ist das so? Ich habe in einem Post vorher gelesen, dass das nur so klappt, nur stand da leider keinerlei Begründung.

Hier nochmal etwas Code:
Delphi-Quellcode:
type
TTest = class(TObject)
private
  FThreadHandle : Integer;
  FThreadID : Cardinal;
public
  procedure StartIt();
  procedure ThreadedMethod(Instance: TTest);
end;

procedure ThreadedProcedure(Instance: TTest);

implementation

// --- here the global procedure ---
procedure ThreadedProcedure(Instance: TTest);
begin
  //
  // < ------- Instance = Self parameter
  //
end;

// --- here the class methods ---
procedure TTest.StartIt();
begin
  // i know, i overwrite the handle and id ;)
  FThreadHandle := BeginThread(nil, 0, @ThreadedProcedure, Pointer(Self), 0, FThreadID);
  FThreadHandle := BeginThread(nil, 0, @TTest.ThreadedMethod, Pointer(Self), 0, FThreadID);
end;

procedure TTest.ThreadedMethod(Instance: TTest);
begin
  //
  // < ------- Instance <> Self parameter
  //
end;

SirThornberry 2. Jul 2010 07:48

AW: Parameterübergabe bei BeginThread für Klassenmethode
 
Das ist ganz einfach.
Deine Methode TTest.ThreadedMethod sieht intern so aus:
Delphi-Quellcode:
procedure TTest.ThreadedMethod(Self: TTest; Instance: TTest);
Es gibt also immer als ersten Parameter den unsichtbaren Self-Parameter. Anders ausgedrückt heißt es das deine Methode incompatibel zur erwarteten Funktion ist die eben nur einen Parameter vom Typ Pointer hat. Man sollte sich aber auch nicht darauf verlassen das es in alle Ewigkeit so aussieht das der erste unsichtbare Parameter Self ist. Denn wenn irgendwann mal in einer neuen Version oder bei einem anderen Compiler das ganze intern anders gehandhabt wird funktionieren dann solche hacks nicht mehr:
Delphi-Quellcode:
procedure TTest.ThreadedMethod();
(also das man eine Parameterlose Methode nimmt wohlwissen das der unsichtbare Parameter Self der einzige ist.

himitsu 2. Jul 2010 07:57

AW: Parameterübergabe bei BeginThread für Klassenmethode
 
oder als statische Klassenmethode:
Delphi-Quellcode:
class procedure ThreadedMethod(Instance: TTest); static;
.

Methoden und "nicht-statische" Klassen-Methoden haben eben dieses unsichtbare "Self" mit drin.
> Methode: Self=Objektinstanz
> Klassen-Methode: Self=Klassen-Typ
> statische (static) Klassen-Methode: Self gibt's nicht

SirThornberry 2. Jul 2010 08:06

AW: Parameterübergabe bei BeginThread für Klassenmethode
 
@himitsu? Bist du sicher das es so funktioniert? Ich hab mir den ASM-Code nicht angesehen aber kann es nicht sein das auch bei Klassenmethoden der unsichtbare Self-Parameter existiert? Nur eben nicht als Instanz sondern der Klasse selbst.

[Edit]
@himitus: Hätte ich deinen Beitrag mal zu Ende gelesen so hätte ich mir diesen Beitrag im gesamten hier sparen können :oops:

Deep-Sea 2. Jul 2010 08:07

AW: Parameterübergabe bei BeginThread für Klassenmethode
 
@SirThornberry:
Hat er doch geschrieben?! :-D

s.h.a.r.k 2. Jul 2010 15:51

AW: Parameterübergabe bei BeginThread für Klassenmethode
 
Zitat:

Zitat von SirThornberry (Beitrag 1032980)
Das ist ganz einfach.
Deine Methode TTest.ThreadedMethod sieht intern so aus:
Delphi-Quellcode:
procedure TTest.ThreadedMethod(Self: TTest; Instance: TTest);
Es gibt also immer als ersten Parameter den unsichtbaren Self-Parameter. Anders ausgedrückt heißt es das deine Methode incompatibel zur erwarteten Funktion ist die eben nur einen Parameter vom Typ Pointer hat. Man sollte sich aber auch nicht darauf verlassen das es in alle Ewigkeit so aussieht das der erste unsichtbare Parameter Self ist. Denn wenn irgendwann mal in einer neuen Version oder bei einem anderen Compiler das ganze intern anders gehandhabt wird funktionieren dann solche hacks nicht mehr:
Delphi-Quellcode:
procedure TTest.ThreadedMethod();
(also das man eine Parameterlose Methode nimmt wohlwissen das der unsichtbare Parameter Self der einzige ist.

Gehe ich richtig in der Annahme, dass, mit aktuellem Compiler, es mit einer Methode ohne Parameter funktioniert?!

himitsu 2. Jul 2010 16:03

AW: Parameterübergabe bei BeginThread für Klassenmethode
 
Das ist bei Delphi eigentlich schon immer so, also daß Klassen-Methoden diesen unsichtbaren Parameter haben.
Irgendwo muß je die Information herkommen, um welche Klasse/Instanz es sich handelt, wenn man eine Methode aufruft.

Der TMethod-Zeiger ist auch um diesen Parameter größer.
(Zeiger auf Methode + die Objektinstanz)

s.h.a.r.k 2. Jul 2010 16:16

AW: Parameterübergabe bei BeginThread für Klassenmethode
 
Nur habe ich diese Information immer nur aus dem DP-Forum. An offizieller Stelle habe ich das noch nie gesehen.

himitsu 2. Jul 2010 18:37

AW: Parameterübergabe bei BeginThread für Klassenmethode
 
Offiziell steh halt einiges nirgends.

z.B. sieht
Delphi-Quellcode:
function test: String;
intern eigentlich so aus
Delphi-Quellcode:
procedure test(var Result: String);
Bei einem Integer sieht es aber anders aus.


Ihr könnt ja gerne mal raten, welche Werte von den Messageboxen angezeigt werden ... mal sehn wer alles richtig tippt. :angel:
Delphi-Quellcode:
function Test: String;
begin
  Result := Result + 'abc ';
  raise Exception.Create('buhh');
end;

function Test2: String;
begin
  Result := Result + 'abc ';
end;

function Test3: Integer;
begin
  Result := 1;
  raise Exception.Create('buhh');
end;

function Test4: Integer;
begin
  Result := Result + 1;
end;

procedure TForm1.FormCreate(Sender: TObject);
var S: String;
  i: Integer;
begin
  try
    S := Test;
  except
    // heut ma nix
  end;
  ShowMessage(S);
  S := 'tja ';
  S := Test2;
  S := Test2;
  ShowMessage(S);

  try
    i := Test3;
  except
    // heut ma nix
  end;
  ShowMessage(IntToStr(i));
  i := 10;
  i := Test4;
  i := Test4;
  ShowMessage(IntToStr(i));
end;
PS: Das ist auch ein gutes Beispiel, warum meistens Variablenwerte immer initialisiert werden sollten.

SirThornberry 3. Jul 2010 10:48

AW: Parameterübergabe bei BeginThread für Klassenmethode
 
Zitat:

Zitat von himitsu (Beitrag 1033151)
...Ihr könnt ja gerne mal raten, welche Werte von den Messageboxen angezeigt werden ...

Der Vollständigkeit halber wäre es schön wenn du uns verraten könntest was bei dir angezeigt wird. Denn wenn in 20 Jahren niemand mehr das gleiche Delphi hat wie jenes welches du derzeit benutzt, kann man nur raten ob es in deren Versionen das gleiche ergibt wie bei dir jetzt.

Wie sieht es eigentlich mit der Aufrufkonvention aus? Ist BeginThread eine Api-Funktion? Wenn ja wird doch sicher stdcall oder cdecl erwartet oder?

himitsu 3. Jul 2010 11:57

AW: Parameterübergabe bei BeginThread für Klassenmethode
 
Es hat sich seit Jahrzehten nichts mehr geändert ... da wird sich wohl so schnell auch nicht dran ändern :roll:

Warum haben wir kein [Spoiler]-Tag?
(sonst hätte ich es vorhin ja schon geschrieben)
Code:
1: "abc "

2: "tja abc abc "

3: zufallswert, da i nicht initialisiert und Result nicht zugewiesen

4: zufallswert + 1, da Result nicht initialisiert

Luckie 3. Jul 2010 12:33

AW: Parameterübergabe bei BeginThread für Klassenmethode
 
[QUOTE=SirThornberry;1033246]
Zitat:

Zitat von himitsu (Beitrag 1033151)
Ist BeginThread eine Api-Funktion?

Nein die API-Funktion ist MSDN-Library durchsuchenCreateThread. BeginThread ist der Delphi-Wrapper, um unter anderem durch setzen von IsMultiThreaded den Heap Thread sicher zumachen.

idefix2 5. Jul 2010 01:24

AW: Parameterübergabe bei BeginThread für Klassenmethode
 
@ himitsu: darauf, dass bei 1. "abc " herauskommt, würde ich mich jedenfalls nicht verlassen. Result wird zwar über den Stack übergeben, aber ob wirklich immer ein push nil erfolgt? Genausogut könnte der generierte Code den Stackpointer direkt erhöhen, ohne einen definierten Wert dort abzulegen, dann löst u.U. schon Result := Result+... eine Exception aus. Turbo Pascal hat das bei Stringresults so gemacht, allerdings waren das short Strings, wo im Stack direkt die Maximallänge des Strings, bis zu 255 bytes + Längenbyte, reserviert wurde und nicht nur ein Pointer.

himitsu 5. Jul 2010 06:04

AW: Parameterübergabe bei BeginThread für Klassenmethode
 
ich sag's ja nicht, daß es immer so ist ... aber bei dem Beispiel isses so, darum dann auch der zweite Durchlauf mit "tja abc abc ", um das Problem besser aufzuzeigen.

auch wenn Stringvariablen für gewöhnlich automatisch initialisiert werden ... man sollte ja eh seine Variablen initialisieren. :stupid:


Alle Zeitangaben in WEZ +1. Es ist jetzt 11:11 Uhr.

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