Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Thread-Parameter auf dem Stack übergeben (https://www.delphipraxis.net/110050-thread-parameter-auf-dem-stack-uebergeben.html)

Luckie 12. Mär 2008 08:52


Thread-Parameter auf dem Stack übergeben
 
Ich kann einem Thread Parameter entweder über den Heap oder über den Stack mitgeben. Übergebe ich sie über den Heap, muss ich Speicher reservieren und im Thread natürlich wieder freigeben.

Aber wie funktioniert das, wenn ich die Parameter über den Stack übergebe? Siehe dazu das Beispiel. Wer gibt da wann und wie den Speicher wieder frei?

Delphi-Quellcode:
type
  TThreadParams = packed record
    Number: Integer;
    Text: String;
  end;
  PThreadParams = ^TThreadParams;

function IntToStr(Int: integer): string;
begin
  Str(Int, result);
end;

function ThreadFunc(tp: PThreadParams): Integer;
var
  Number: Integer;
  Text: String;
  s: String;
begin
  Number := PThreadParams(tp)^.Number;
  Text := PThreadParams(tp)^.Text;
  s := 'Zahl: ' + IntToStr(Number) + #13#10 + 'Text: ' + Text;
  Result := MessageBox(0, PChar(s), 'Thread', MB_YESNO or MB_ICONINFORMATION);
end;

var
  tp: TThreadParams;
  Thread: THandle;
  ThreadID: Cardinal;
  ExitCode: Cardinal;

begin
  tp.Number := 42;
  tp.Text := 'Die Antwort.';
  Thread := BeginThread(nil, 0, @ThreadFunc, @tp, 0, ThreadID);
  WaitForSingleObject(Thread, INFINITE);
  GetExitCodeThread(Thread, ExitCode);
  case ExitCode of
    IDYES: Writeln('Benutrzer hat "Ja" angeklickt.');
    IDNO: Writeln('Benutzer hat "Nein" angeklickt.');
  end;
  CloseHandle(Thread);
  Readln;
end.

Dax 12. Mär 2008 09:00

Re: TRhread-Parameter auf dem Stack übergeben
 
Das wird so nicht gehen. Du musst dafür sorgen, dass die Parameter auf dem Stack nicht überschrieben werden, während der Thread darauf zugreift - was im schlimmsten Fall über die gesamte Threadlaufzeit geschieht. Für diese Zeit musst du auf den Thread warten, da ein Return aus der aktuellen Methode dafür Sorgen würde, dass dein Parameter Freiwild wird und vom nächsten Aufruf im erzeugenden Thread höchstwahrscheinlich gründlich zerlegt wird. Was natürlich bedeuten würde, dass Threads in dem Fall eher.. naja, sinnlos sind.

Aber zum eigentlich Thema: den Speicher freigeben muss der Aufrufer, wenn es der Thread nicht tut. Da du den Speicher über den Stack holst, musst du ihn *garnicht* freigeben, du darfst es sogar nicht mal, nicht im Thread und nirgendwo sonst, das macht nämlich deine Methode automatisch beim Return.

sirius 12. Mär 2008 09:03

Re: Thread-Parameter auf dem Stack übergeben
 
Derzeit übergibst du eine globale Variable.
Solange du im Mainthread die golabel Variable nicht änderst (was du ja auf Grund von WaitForsingleObject ja erriechst) kein Problem.
Wenn du den Parameter wirklich auf dem Stack hast (loakle Variable) wird diese spätestens nach Abschluss der Prozedur ungültig.

Also nur Heap benutzen!

Luckie 12. Mär 2008 09:05

Re: Thread-Parameter auf dem Stack übergeben
 
Dass es ohne warten nicht funktionieren würde ist klar, aber das tue ich ja in meinem Fall. Und was das Freigeben angeht, der Speicher auf dem Stack wird beim Verlassen der Funktion automatisch vom Betriebssystem aufgeräumt. Habe ich das so richtig verstanden?

@sirius: Stimmt, hier ist sie ja global, also auf dem Heap. Dann muss ich das noch mal umbauen.

So sollte es eigentlich sein:

Delphi-Quellcode:
function ThreadFunc(tp: PThreadParams): Integer;
var
  Number: Integer;
  Text: String;
  s: String;
begin
  Number := PThreadParams(tp)^.Number;
  Text := PThreadParams(tp)^.Text;
  s := 'Zahl: ' + IntToStr(Number) + #13#10 + 'Text: ' + Text;
  Result := MessageBox(0, PChar(s), 'Thread', MB_YESNO or MB_ICONINFORMATION);
end;

procedure RunThread;
var
  tp: TThreadParams;
  Thread: THandle;
  ThreadID: Cardinal;
  ExitCode: Cardinal;
begin
  tp.Number := 42;
  tp.Text := 'Die Antwort.';
  Thread := BeginThread(nil, 0, @ThreadFunc, @tp, 0, ThreadID);
  WaitForSingleObject(Thread, INFINITE);
  GetExitCodeThread(Thread, ExitCode);
  case ExitCode of
    IDYES: Writeln('Benutrzer hat "Ja" angeklickt.');
    IDNO: Writeln('Benutzer hat "Nein" angeklickt.');
  end;
  CloseHandle(Thread);
end;

SirThornberry 12. Mär 2008 09:07

Re: Thread-Parameter auf dem Stack übergeben
 
du übergibst den Parameter ja gar nicht über den Stack. Da es eine globale Variable ist liegt diese nicht auf dem Stack. Aber selbst wenn sie auf dem Stack liegen würde übergibst du sie nicht über den Stack sondern du übergibst ja dem Thread generell nur einen Pointer auf Daten die irgendwo liegen. Und du musst nur dafür sorgen das die Daten worauf der Pointer zeigt auch noch da sind wenn über den Pointer darauf zugegriffen wird.

Dadurch das du auf den thread wartest bis er beendet ist würde der Inhalt auf dem Stack auch unverändert bleiben.

Der Stack wird übrigens nicht vom Betriebssystem aufgeräumt. Je nach Calling Convention wird der Stack dort aufgeräumt wo die Funktion aufgerufen wurde oder eben in der Funktion. Wenn du dir den asm-code ansiehst wirst du sehen das explizit funktionen aufgerufen werden die den Stack auf- und abbauen.

Dax 12. Mär 2008 09:13

Re: Thread-Parameter auf dem Stack übergeben
 
Zitat:

Zitat von Luckie
Und was das Freigeben angeht, der Speicher auf dem Stack wird beim Verlassen der Funktion automatisch vom Betriebssystem aufgeräumt. Habe ich das so richtig verstanden?

Nein. Das Betriebssystem hat damit recht wenig zu tun, der von der Methode belegte Stackspeicher wird von der Methode selbst beim Return wieder an den Stack zurückgegeben.

Ein kleines Beispiel dazu:

-Eintritt in die Methode: SP steht auf 1000
-Methode schnapp sich 100 Byte Stackspeicher: SP auf 1100
-Method gedönst rum...
-Method gibt Stackspeicher frei: SP wieder auf 1000
-ret: SP auf 996 (für 32er-Systeme)

Luckie 12. Mär 2008 09:15

Re: Thread-Parameter auf dem Stack übergeben
 
Ich habe das Beispiel geändert. Jetzt liegt der Thread-Parameter auf dem Stack. Damit dass das Betriebssystem den Stack aufräumt, hab eich mich wohl etwas ungenau ausgedrückt. Ich wollte damit ausdrücken, dass dies automatisch geschieht ohne dass man als zusätzlichen, eigenen Code dafür schreiben müsste, wie man es muss, wenn man den Parameter über den Heap übergibt.

SirThornberry 12. Mär 2008 09:16

Re: Thread-Parameter auf dem Stack übergeben
 
bischen Krümelkacken: Der stackpointer läuft rückwärts, wird also von oben nach unten gefüllt. Pack man was auf den Stack drauf wird der Stackpointer decrementiert (zeigt also auf eine niederigere Adresse als vorher)

sirius 12. Mär 2008 09:16

Re: Thread-Parameter auf dem Stack übergeben
 
Zitat:

Zitat von Luckie
@sirius: Stimmt, hier ist sie ja global, also auf dem Heap. Dann muss ich das noch mal umbauen.

Nee globale (nicht dynamische) Varibalen sind nicht auf dem Heap. sie sind schon Teil der EXE und werden so im Datensegment mit reingeladen.

Und als Ergänzung noch zu Dax:
Auf Grund von Codeoptimierung kann es passieren, dass die Gültigkeit schon vor Ablauf der Funktion aufgehoben wird und der Stackplatz von einer anderen lokalen Variable belegt wird.

SirThornberry 12. Mär 2008 09:19

Re: Thread-Parameter auf dem Stack übergeben
 
@sirius: Die Codeoptimierung sollte aber auch berücksichtigen das ein Pointer auf die lokale Variable verwendet wurde und somit die Gültigkeit länger anhält. In Sprachen wie C gibt es dafür volatile um das weg optimieren zu verhindern (zum Beispiel für so einen Fall wenn ein anderer Thread noch darauf zugreifen könnte). In Delphi ist mir diesbezüglich nichts bekannt.


Alle Zeitangaben in WEZ +1. Es ist jetzt 16:03 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