Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Multithreading und Globale Funktionen (https://www.delphipraxis.net/157286-multithreading-und-globale-funktionen.html)

schwa226 6. Jan 2011 16:26

Multithreading und Globale Funktionen
 
Hallo!

Ich muss einmal nachfragen bevor ich einen Blödsinn mache!

Ich habe mehrere Threads.
In einer globalen Unit habe ich dann z.B. diese Funktion:
Delphi-Quellcode:
function Add(a,b : Integer): Integer;
begin
   Result := (a + b);
end;
Kann ich diese einfach im Thread verwenden oder kann es da zu Problemen führen?
Es kann ja sein, dass der eine Thread gerade in der Funktion ist und dann ein anderer reinkommt.

mkinzler 6. Jan 2011 16:29

AW: Multithreading und Globale Funktionen
 
Das führt sogar mit großer Wahrscheinlichkeit zu Problemen.

generic 6. Jan 2011 16:35

AW: Multithreading und Globale Funktionen
 
Zitat:

Zitat von mkinzler (Beitrag 1072678)
Das führt sogar mit großer Wahrscheinlichkeit zu Problemen.

Wieso?

Bei jedem Aufruf einer Funktion, werden die lokalen Variablen neu instantiiert. Jeder Aufrufer hat also seine eigene Instanz.

In dem Beispiel hier gibt es noch nicht einmal Variablen.

Anders würde es Aussehen bei globalen Vars.

Assarbad 6. Jan 2011 16:40

AW: Multithreading und Globale Funktionen
 
Zitat:

Zitat von generic (Beitrag 1072681)
Zitat:

Zitat von mkinzler (Beitrag 1072678)
Das führt sogar mit großer Wahrscheinlichkeit zu Problemen.

Wieso?

Ich denke er meinte den Aufrufer. Diese Funktion innerhalb eines Threads aufrufen sollte kein Problem sein. Aber aufgrund dieser einen Funktion kann man kaum eine Aussage treffen, bzw. die welche du gerade gemacht hast ...

schwa226 6. Jan 2011 16:46

AW: Multithreading und Globale Funktionen
 
Die Funktion(en) haben natürlich auch Vars.
War ja nur ein Beispiel.
Wenn aber für jeden Thread beim Aufruf eine eigene Instanz gemacht wird könnte es funktionieren.

Das mit den globalen Variabeln ist mir dann auch klar!

generic 6. Jan 2011 16:48

AW: Multithreading und Globale Funktionen
 
Eigentlich muss man sich die Grundfrage stellen, ob zwei Threads gleichzeitig auf die gleiche Variable zugreifen können.

Bei lokalen Variablen und bei Parametern schwierig, es sein denn das Referenzen (z.B. Objekte -> also Zeiger) übergibt.

schwa226 6. Jan 2011 16:52

AW: Multithreading und Globale Funktionen
 
Ok,

die meisten globalen Vars sind eh TList und ich übergebe das Item als Pointer.

Gefärlich wird es natürlich wenn die TList geändert wird aber ein anderer Thread noch damit arbeitet - also wenn das Item dann vielleicht gar nicht mehr da ist.

Wie löst man dann sowas? Jeden Thread eine Kopie der TList geben. Die TList nur aus dem MainThread bearbeiten und nach dem Bearbeiten dem Thread die neue Liste übergeben!?

messie 6. Jan 2011 17:01

AW: Multithreading und Globale Funktionen
 
Zitat:

Zitat von schwa226 (Beitrag 1072689)
Wie löst man dann sowas? Jeden Thread eine Kopie der TList geben. Die TList nur aus dem MainThread bearbeiten und nach dem Bearbeiten dem Thread die neue Liste übergeben!?

Nee, selbst der Kopiervorgang kann während einer Bearbeitung fehlschlagen. Du musst die List während des Zugriffs schützen. Da gibt es verschiedene Möglichkeiten.
Suche mal nach "Thread synchronisieren". Stichworte sind Semaphoren, critical sections, Mutex

Grüße, Messie

generic 6. Jan 2011 19:53

AW: Multithreading und Globale Funktionen
 
In Delphi gibt es eine spezialisierte TList dafür.
TThreadList

schwa226 9. Jan 2011 14:04

AW: Multithreading und Globale Funktionen
 
Zitat:

Zitat von generic (Beitrag 1072769)
In Delphi gibt es eine spezialisierte TList dafür.
TThreadList

Danke für den Tipp!

Habe es nun eingebaut, jedoch geht es in einem Thread - im anderen nicht!?

Ich mache es so in den Threads:
Delphi-Quellcode:
var
   List : TList;
   i : Integer;
begin

   List := ALL_Names.Locklist;

   for i := 0 to List.Count - 1 do
   begin
      //do stuff with data
   end;
   ALL_Names.UnlockList;
Es bleibt beim Locklist stehen.

Der einzige Unerschied ist, dass es im dem Thread wo es nicht geht in der Thread.Execute ausgeführt wird.
Im Thread wo es geht wird es per ein OnUDPRead Thread ausgeführt. Also der Thread hat einen UDP-Server mit einer OnUDPRead vom Thread. Da geht es ohne Probleme.
Kann das LockList nicht im Execute vom Thread ausgeführt werden weil der Thread dann stehen bleibt und das Locklist nicht durchgeführt werden kann??

Ein zusätzliches ALL_Names.UnlockList davor bringt auch nichts.

alzaimar 9. Jan 2011 14:13

AW: Multithreading und Globale Funktionen
 
Grundsätzlich musst Du die Aufrufe LockList/Unlocklist ein einem Resourcenschutzblock kapseln. Das geht so:
Delphi-Quellcode:
MyLocalList := TheThreadList.LockList;
Try
  DoSomethingWith(MyLocalList);
Finally
  TheThreadList.UnlockList;
End;
Natürlich hängt der zweite Thread im LockList. Nämlich genausolange, wie der erste Thread sein UnlockList noch nicht aufgerufen hat.
Mit dem Aufruf von "Locklist" öffnet Du die Tür zur Liste, zieht aber den Schlüssel von der Tür ab und macht hinter dir wieder zu.
"Unlocklist" verlässt die Liste, schließt wieder ab und hinterlässt den Schlüssel.

Der Aufruf von "LockList" wartet so lange, bis der Schlüssel wieder in der Tür steckt.

Klaus01 9. Jan 2011 14:19

AW: Multithreading und Globale Funktionen
 
Hallo,

wenn im anderen Thread die List gesperrt ist (locked)
dann kann der zweite Thread nicht darauf zugreifen.

Also nur die List sperren wenn Du darauf zugreifen willst.

Grüße
Klaus

schwa226 9. Jan 2011 18:00

AW: Multithreading und Globale Funktionen
 
So schaut mein Thread (gekürzt) aus:
Delphi-Quellcode:
constructor TNames_thread.Create(const MainWindowHandle : Thandle);
begin
  inherited create(True); // CreateSuspended = true
  FMainHandle := MainWindowHandle;

  FreeOnTerminate := False;
end;


procedure TNames_thread.Execute();
var
  Msg : TMsg;
  List: TList;
  len : Integer;
begin
  while (not Terminated) do
  begin

      List := All_Names.Locklist;
      try
         len := List.Count;
      finally
         All_Names.Unlocklist;
      end;

    Sleep(1000);
  end; // while (not Terminated) do

  Terminate;
end;
Wenn der Thread nun das Locklist aufruft steht er!?
Das ist bei beiden Threads gleich.

Der was geht hat ja die OnUDPRead vom UDP Server und diese Funktion wird von einem andern Thread (TIdUDPListenerThread) ausgeführt. Dann kann das Locklist durchgeführt werden.

Die All_Names Liste wird in der Mainform in der OnCreate erzeugt.

EDIT:
Fehler gefunden!
Ich habe in der OnCreate im MainThread zuerst ein All_Names.Locklist.clear ausgeführt. danach aber kein Unlock!

Jetzt geht's!

schwa226 14. Feb 2011 13:16

AW: Multithreading und Globale Funktionen
 
Jetzt habe ich noch eine Frage zu dem Thema, aber mit einer anderen Funktion:

Delphi-Quellcode:
function GetString(Data : TBytes):String;
var
  i : Integer;
begin
  Result := '';
  for i := 0 to High(Data) do
    Result := Result + IntToHex(Data[i], 2);
end;
Kann man sowas für Multithread verwenden? Oder wird da das Result zurückgesetzt wenn gerade ein Thread in der For Schleife ist und ein anderer die GetString ausführt?

Oder muss es dann so gemacht werden:
Delphi-Quellcode:
function GetString(Data : TBytes):String;
var
  i : Integer;
begin
  EnterCriticalSection(CS);
  try
    Result := '';
    for i := 0 to High(Data) do
      Result := Result + IntToHex(Data[i], 2);
  finally
    LeaveCriticalSection(CS);
  end;
end;
Ich will sozusagen einen Logger machen. Wenn ich nun CriticalSection verwende ist der eine Thread solange blockiert bis der andere Thread durch ist. Dies führt zu einer nicht gewollten Verzögerung wenn es jetzt um viele Daten geht.

Assarbad 14. Feb 2011 13:22

AW: Multithreading und Globale Funktionen
 
Das kommt auf die Daten an. Wenn diese Daten von mehreren Threads verändert/gelesen werden, muß eine Synchronisation stattfinden, ansonsten nicht. Es gibt zT sehr clevere Ansätze um parallel an großen Datensätzen zu arbeiten ohne daß sich die Threads in die Quere kommen.

Eine Frage hier wäre bspw., ob sich "Data" (durch einen anderen Thread) ändert, während es von dieser Funktion verarbeitet wird. Ohne weitere Infos kann man da keine konkreten Aussagen machen.

schwa226 14. Feb 2011 13:30

AW: Multithreading und Globale Funktionen
 
Ne, Data gehört zu jedem Thread selber der die Funktion aufruft.
D.h. jeder Thread ruft die Funktion auf mit seinen eigenen privat deklariertden Data mit unterschiedlicher länge. Ich bin mir halt nicht sicher, wenn sich schon ein Thread in der Funktion befindet das Result von einem anderen Thread wieder mit dem Result :=''; wieder zurückgesetzt wird. Oder hat jeder Aufruf sein eigenes Result? Oder ob dadurch das High(Data) auch verbogen wird?
Lokale Variabeln gelten ja für jeden eigenen Aufruf/Thread selber.

Assarbad 14. Feb 2011 13:38

AW: Multithreading und Globale Funktionen
 
Zitat:

Zitat von schwa226 (Beitrag 1081582)
Ne, Data gehört zu jedem Thread selber der die Funktion aufruft.
D.h. jeder Thread ruft die Funktion auf mit seinen eigenen privat deklariertden Data mit unterschiedlicher länge. Ich bin mir halt nicht sicher, wenn sich schon ein Thread in der Funktion befindet das Result von einem anderen Thread wieder mit dem Result :=''; wieder zurückgesetzt wird. Oder hat jeder Aufruf sein eigenes Result? Oder ob dadurch das High(Data) auch verbogen wird?

Ob High(Data) dort funktioniert kann ich ohne Testen nicht sagen, ich ging davon aus, daß dies nur bei bestimmten Arraytypen geht ... ansonsten sollte es keine Rolle spielen ob verschiedene Threads die gleiche Funktion aufrufen. Funktionen sind nur Code. Die Daten sind entweder auf dem Heap oder Stack, also vom Code getrennt ...

Zitat:

Zitat von schwa226 (Beitrag 1081582)
Lokale Variabeln gelten ja für jeden eigenen Aufruf/Thread selber.

Exakt. Jeder Thread hat seinen eigenen Stack und damit eigene Variablen.

schwa226 14. Feb 2011 13:45

AW: Multithreading und Globale Funktionen
 
Ok, danke!

Dann werde ich das mal probieren und sehen ob es zu Exceptions kommt.

Mit dem "verbiegen" des High(Data) hatte ich gemeint, wenn der erste Thread z.B. mit Data[len=500] aufruft. Der zweite dann mit Data[len=3000]. Wenn nun auch i beim ersten Thread bis 2999 raufgezählt wird, dann gibt's natürlich Probleme weil der erste ja nur bis 499 geht.

Assarbad 14. Feb 2011 13:48

AW: Multithreading und Globale Funktionen
 
Zitat:

Zitat von schwa226 (Beitrag 1081588)
Mit dem "verbiegen" des High(Data) hatte ich gemeint, wenn der erste Thread z.B. mit Data[len=500] aufruft. Der zweite dann mit Data[len=3000]. Wenn nun auch i beim ersten Thread bis 2999 raufgezählt wird, dann gibt's natürlich Probleme weil der erste ja nur bis 499 geht.

Hab das schon verstanden. Aber gibt es denn keine Variante bei der du diese Daten explizit mitgeben kannst? Bspw. in Form von Parametern.

himitsu 14. Feb 2011 13:53

AW: Multithreading und Globale Funktionen
 
Bremst weniger aus.
Delphi-Quellcode:
function GetString(Data : TBytes):String;
var
  Temp: TBytes;
  i : Integer;
begin
  EnterCriticalSection(CS);
  try
    Temp := Copy(Data)
  finally
    LeaveCriticalSection(CS);
  end;
  Result := '';
  for i := 0 to High(Temp) do
    Result := Result + IntToHex(Temp[i], 2);
end;
Wobei ich mich immer frage, wie man auf soeine "kranke" Schleife kommt, mit soeiner Masse an langsamen Stringoperationen. :shock:
> Delphi-Referenz durchsuchenBinToHex

Abgesehn davon würde ich nicht den Data-Parameter auf dieser Seite absichern, sondern ich würde auf der aufrufenden Seite das absichern, welches man an Data übergibt.

jfheins 14. Feb 2011 13:57

AW: Multithreading und Globale Funktionen
 
Zitat:

Zitat von schwa226 (Beitrag 1081582)
Ne, Data gehört zu jedem Thread selber der die Funktion aufruft.
D.h. jeder Thread ruft die Funktion auf mit seinen eigenen privat deklariertden Data mit unterschiedlicher länge. Ich bin mir halt nicht sicher, wenn sich schon ein Thread in der Funktion befindet das Result von einem anderen Thread wieder mit dem Result :=''; wieder zurückgesetzt wird. Oder hat jeder Aufruf sein eigenes Result? Oder ob dadurch das High(Data) auch verbogen wird?
Lokale Variabeln gelten ja für jeden eigenen Aufruf/Thread selber.

Damit (und dass jeder Thread einen eigenen Stack hat) ist doch alles gesagt? Es braucht keine Critical Section, da hier kein Zugriff auf gemeinsame Speicherbereiche erfolgt.
Auch High() wird dir immer das richtige Ergebnis zurückliefern. Wäre auch schlimm wenn nicht ;)

Zitat:

Zitat von schwa226 (Beitrag 1081588)
Ok, danke!
Dann werde ich das mal probieren und sehen ob es zu Exceptions kommt.
Mit dem "verbiegen" des High(Data) hatte ich gemeint, wenn der erste Thread z.B. mit Data[len=500] aufruft. Der zweite dann mit Data[len=3000]. Wenn nun auch i beim ersten Thread bis 2999 raufgezählt wird, dann gibt's natürlich Probleme weil der erste ja nur bis 499 geht.

Jeder Thread hat seine eigene Schleifenvariable i. Und auch der Vergleichswert (also das Ergebnis von High()) hat jeder Thread für sich.

Assarbad 14. Feb 2011 14:01

AW: Multithreading und Globale Funktionen
 
Zitat:

Zitat von jfheins (Beitrag 1081594)
Auch High() wird dir immer das richtige Ergebnis zurückliefern.

Woher nimmst du diese Zuversicht?
Code:
TBytes = array of Byte;
... in Sysutils.pas (RAD Studio 2009). Gibt's da irgendwelche Compiler-Magic oder wie weiß die aufgerufene Funktion über die Länge bescheid? Läuft das wie bei Strings? Habe mir nur noch nicht genügend Gedanken drüber gemacht, daher die Frage.

schwa226 14. Feb 2011 14:02

AW: Multithreading und Globale Funktionen
 
Danke für den Tipp mit BinToHex!
Das habe ich bis jetzt noch nie verwendet :oops:

Ich habe mir die Strings halt immer so aus den TBytes erzeugt.

Danke auch an jfheins!

jfheins 14. Feb 2011 15:00

AW: Multithreading und Globale Funktionen
 
Zitat:

Zitat von Assarbad (Beitrag 1081597)
Zitat:

Zitat von jfheins (Beitrag 1081594)
Auch High() wird dir immer das richtige Ergebnis zurückliefern.

Woher nimmst du diese Zuversicht?
Code:
TBytes = array of Byte;
... in Sysutils.pas (RAD Studio 2009). Gibt's da irgendwelche Compiler-Magic oder wie weiß die aufgerufene Funktion über die Länge bescheid? Läuft das wie bei Strings? Habe mir nur noch nicht genügend Gedanken drüber gemacht, daher die Frage.

Sowei ich weis, läuft das bei Strings ähnlich: Die Variable ist ein Pointer auf das erste Element, aber vorher (also an einem negativen Offset) wird die Länge seperat gespeichert. length() liest eben diesen Integer (oder sowas) aus und liefert den zurück. Da jeder Thread sein eigenes Array hat, gibt es keine Konflikte ;)

Assarbad 14. Feb 2011 16:05

AW: Multithreading und Globale Funktionen
 
Zitat:

Zitat von jfheins (Beitrag 1081616)
Sowei ich weis, läuft das bei Strings ähnlich: Die Variable ist ein Pointer auf das erste Element, aber vorher (also an einem negativen Offset) wird die Länge seperat gespeichert. length() liest eben diesen Integer (oder sowas) aus und liefert den zurück. Da jeder Thread sein eigenes Array hat, gibt es keine Konflikte ;)

Okidok. Danke für die Info ;)

Muß wohl wieder mal Delphiprogramme reversen. Die kommen einem im natürlichen Habitat so selten unter ... muß´ also vorher selber schreiben und dann reversen :zwinker:

schwa226 14. Feb 2011 17:54

AW: Multithreading und Globale Funktionen
 
Ich habe es nun auch live getestet.
3 Threads die die Funktion gleichzeitig nutzen. Ohne Critical Section.
Keine Probleme!

Sir Rufo 14. Feb 2011 21:18

AW: Multithreading und Globale Funktionen
 
Funktionen und Prozeduren sind von Haus immer threadsafe, weil alle lokalen Variablen bei jedem Aufruf neu erzeugt werden. Wäre dem nicht so, könnte man auch keine rekursiven Aufrufe von Proc und Funk machen.

Das Problem sind immer nur die Daten auf die dabei zugegriffen wird.
Also alles was nicht lokal zur Proc/Func gehört kann potentiell kritisch sein.


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

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