Delphi-PRAXiS
Seite 1 von 4  1 23     Letzte » 

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Änderungen von Variablen synchronisieren (https://www.delphipraxis.net/167197-aenderungen-von-variablen-synchronisieren.html)

Breager 17. Mär 2012 09:58

Änderungen von Variablen synchronisieren
 
Hallo,

ich hoffe, der Titel ist treffend. Ich habe eine Variable vom Typ String, die sowohl innerhalb eines Threads als auch außerhalb des Threads verändert werden kann. Die Wahrscheinlichkeit ist wohl sehr gering, aber ich befürchte, dass es theoretisch zu Problemen kommen könnte, wenn ich versuche, den Wert dieser Variablen gleichzeitig innerhalb des Threads und außerhalb des Threads zu ändern.

Hier mein Lösungsversuch:
Delphi-Quellcode:
Var ThreadString:AnsiString;
    TreadString_Locked:Boolean;

//Innerhalb des Threads
Begin
...
 ThreadString_Locked:=True;
 ThreadString:='Neuer Wert';
 ThreadString_Locked:=False;
...
End;

//außerhalb des Threads:

Function SetThreadString(s:AnsiString):Boolean;
Var TimeOut:Integer;
Begin
 TimeOut:=0;
 Repeat
 IF not(ThreadString_Locked) then
    BEGIN
     ThreadString:=s;
     Result:=True;
    END ELSE
     Result:=False;

 Inc(TimeOut);
Until TimeOut>1000;
End;
Solange der Wert innerhalb des Threads durch die Variable ThreadString_Locked gesperrt ist, kann der Variablen außerhalb des Threads nichts zugewiesen werden.

Ist dieser Ansatz sinnvoll bzw., gibt es eine einfachere oder besser Lösung?

Gruß

Sir Rufo 17. Mär 2012 10:11

AW: Änderungen von Variablen synchronisieren
 
Jo, z.B. Delphi-Referenz durchsuchenTCriticalSection ;)

und auch mal hier schauen Hier im Forum suchenTCriticalSection

Aphton 17. Mär 2012 11:16

AW: Änderungen von Variablen synchronisieren
 
Apropos Timeout -> du hast da nen Vergleich mit 1000 gemacht, was evt. impliziert, dass du wirklich 1000 ms (1 sek) warten möchtest. Das ist aber nicht der Fall.. Das sind einfach 1000 Schleifendurchläufe; daher ist das auch abhängig von der Prozessorgeschwindigkeit. Verarbeitet dein Prozessor einen Schleifendurchgang in genau einer ms, dann würde das stimmen, nur ist das nicht der Fall. Diese Schleife ist binnen kürzester Zeit durchlaufen (<10ms?)

...nur ne Kleinigkeit ^^

himitsu 17. Mär 2012 12:47

AW: Änderungen von Variablen synchronisieren
 
Booleans sind nicht threadsicher, vorallem da sie nichtmal atomar änderbar sind, da die CPU mir kompletten Registern arbeitet und dann nur die Bytes raus ANDed und SCHIFTet. :stupid:
Die CPUs kennen da einen speziellen Befehl LOCK

z.B.
Delphi-Quellcode:
LOCK MOV [EAX], EDX
statt
Delphi-Quellcode:
MOV [EAX], EDX
.
Und die WinAPI kennt die Interlocked-Behle, wie MSDN-Library durchsuchenInterlockedExchange.


Deine "innere" Änderung von ThreadString ist nicht abgesichert, denn da wird nicht geprüft, ob der String gerade von SetThreadString verändert wird.

Gut, man könnte jetzt beide Zeiten absichern und dann äußere und inner Änderungen nur über dieses SetThreadString ausführen,
Delphi-Quellcode:
Function SetThreadString(s:AnsiString):Boolean;
Var TimeOut:Integer;
Begin
 TimeOut:=0;
 Repeat
   IF not(ThreadString_Locked) then
    BEGIN
     ThreadString_Locked:=True;
     ThreadString:=s;
     ThreadString_Locked:=False;
     Result:=True;
    END ELSE
     Result:=False;

 Inc(TimeOut);
Until TimeOut>1000;
End;
aber hier kann es immernoch vorkommen, daß mehrere Threads gleichzeitig durch das IF kommen und :=True setzen, da diese Beiden erst Recht nicht atomar sind.

Ich hatte letzens die CriticalSection über TryEnter und eine Schleife mit einem TimeOut versehn.
Schön ist das nicht unbedingt, aber es funktioniert wenigstens.

Breager 17. Mär 2012 17:50

AW: Änderungen von Variablen synchronisieren
 
Vielen Dank für Eure Antworten!

Mir brummt schon ganz schön der Schädel. Vielleicht warte ich doch lieber, bis der Thread beendet wurde ;-)

Brauche ich zum Lesen auch eine CriticalSection? Wird der Code innerhalb der CriticalSection in eine Warteschleife gelegt und später abgearbeitet oder einfach nicht ausgeführt/ignoriert? Letzteres wäre vorallem beim Lesen ungünstig.

Zitat:

Zitat von himitsu
Ich hatte letzens die CriticalSection über TryEnter und eine Schleife mit einem TimeOut versehn.

Danke. Im Grunde also das, was ich mit meinem Code erreichen möchte (ein Pollen sozusagen).

Bin gerade über TMultiReadExclusiveWriteSynchronizer gestolpert, was haltet ihr davon? Ich muss den Wert vorwiegend lesen.

Sir Rufo 17. Mär 2012 20:01

AW: Änderungen von Variablen synchronisieren
 
Eigentlich ist das mit der TCriticalSection sehr einfach.

Stell dir eine Kreuzung vor. Die beiden Straßen sind einmal der MainThread und der Thread den du laufen lässt. Die CriticalSection ist nun die Ampel, die dafür sorgt, dass über den Kreuzungspunkt (Zugriff auf die Variable) immer nur eine Fahrbahn (Thread) freigegeben ist.

Delphi-Quellcode:
TMyThread = class( TThread )
strict private
  FCS : TCriticalSection;
private
  FIntStr : string;
  procedure SetIntStr( const Value : string );
  function GetIntStr : string;
protected
  procedure Execute; override;
public
  property IntStr : string read GetIntStr write SetIntStr;
end;

...

procedure SetIntStr( const Value : string );
begin
  FCS.Enter; // Betreten der CS
  try
    FIntStr := Value;
  finally
    FCS.Leave; // Verlassen der CS
  end;
end;

function GetIntStr : string;
begin
  FCS.Enter; // Betreten der CS
  try
    Result := FIntStr;
  finally
    FCS.Leave; // Verlassen der CS
  end;
end;
Bei
Delphi-Quellcode:
FCS.Enter
wird solange gewartet, bis die CriticalSection frei ist und dann betreten.
Innerhalb eines Thread-Kontexts darf die CS sooft betreten werden, wie man möchte. Die CS sperrt ja nur gegen Zugriffe durch unterschiedliche Thread-Kontexte.

Die Getter und Setter der Eigenschaft kümmern sich nun darum, dass der Zugriff geregelt wird, egal aus welchem Thread-Kontext man darauf zugreift.
Aber ... innerhalb des Threads könnte man ja direkt auf
Delphi-Quellcode:
FIntStr
zugreifen, aber dieser Zugriff ist nicht abgesicht (über die CS), also sollte der Zugriff auch innerhalb des Threads über die Eigenschaft und nicht über das Feld erfolgen.
Delphi-Quellcode:
procedure TMyThread.Execute;
begin
  while not Terminated do
  begin
    ...
    // FIntStr := 'So nicht';
    IntStr := 'So geht das';
    ...
  end;
end;
end;
Der Vollständigkeit halber sei noch erwähnt, dass die Instanz von FCS natürlich von TMyThread im Constructor erzeugt und im Destructor wieder freigegeben werden sollte bzw. muss.

Breager 17. Mär 2012 20:53

AW: Änderungen von Variablen synchronisieren
 
Danke Sir Rufo :-D

Habs gerade selbst getestet und gesehen, dass alles schön sauber nacheinander abgearbeitet wird.
Ich benutze in meinem Programm eine TList, dessen Items ich jeweils einen Record zuweise. Ein Bestandteil dieses Records ist die String-Variable (in meinem Beispiel ThreadStrg).

Jedes Bearbeiten der Liste (Add, Insert, Delete, Exchange, Clear) im Hauptthread und natürlich das Ändern sowie das Lesen des Strings im Thread und im Hauptthread muss ich also einfach in die CriticalSection packen.
Zitat:

Zitat von Sir Rufo
Der Vollständigkeit halber sei noch erwähnt, dass die Instanz von FCS natürlich von TMyThread im Constructor erzeugt und im Destructor wieder freigegeben werden sollte bzw. muss.

Außerdem muss die Unit SyncObjs eingebunden werden.

Für meinen Fall ist es dann wahrscheinlich am sinnvollsten, die Instanz FCS zusammen mit TList zu erstellen und wieder freizugeben?

@Aphton: Danke für Deinen Hinweis. Das ist mir schon klar. Für ein zeitgebundenes Timeout würde ich etwas anders an die Sache rangehen.

himitsu 17. Mär 2012 22:56

AW: Änderungen von Variablen synchronisieren
 
Microsoft hat leider die Verwaltung der CS umgestllt.
In den Debuginfos der CS gibt es eigentlich ein Event-Objekt, an welches ich mich dranhängen wollte, um nicht pollen zu müssen, aber leider ist dort, zumindestens in Win7/Server2008/Server2011 immer nur "Nichts" (0) eingetragen. :cry:

In der Unit SyncObjs gibt es auch Objekte, welche ein TimeOut verwenden können.



PS: Deine 1000-Schleifendurchläufe dürften schneller sein, als die Speicherverwaltung eines Strings, was sich etwas ungünstig auswirken dürfte.

Breager 18. Mär 2012 00:46

AW: Änderungen von Variablen synchronisieren
 
Danke himitsu!

Das klingt nicht so gut :(

Hier mal ein Versuch, das ganze mit InterlockedExchange sicher zu machen:
Delphi-Quellcode:
Var ThreadString:AnsiString;
    ThreadString_Locked:Integer;
...
ThreadString_Locked:=Integer(False);
...

Function SetThreadString(S:AnsiString):Boolean;
Var TimeOut:Integer;
Begin
 TimeOut:=0;
 Repeat
  IF InterlockedExchange(ThreadString_Locked,Integer(True)) = 0 then
     BEGIN
      ThreadString:=S;
      InterlockedExchange(ThreadString_Locked,Integer(False));
      Result:=True;
     END ELSE
      Result:=False;
  Inc(TimeOut);
 Until (TimeOut>1000) OR Result;
End;
Das weitere Verarbeiten der Liste (Sortieren, etc.) findet nur im Mainthread statt. Hierbei müsste ich einfach überprüfen, ob der Thread aktiv ist oder ThreadString_Locked auf True gesetzt ist.

Mal noch eine generelle Frage: Solange ich den Wert einer globalen Variablen nur innerhalb "eines" Threads ändere, kann doch eigentlich nicht passieren, und beim Lesen doch sowieso nichts?

Sir Rufo 18. Mär 2012 01:17

AW: Änderungen von Variablen synchronisieren
 
Zitat:

Zitat von Breager (Beitrag 1157123)
Mal noch eine generelle Frage: Solange ich den Wert einer globalen Variablen nur innerhalb "eines" Threads ändere, kann doch eigentlich nicht passieren, und beim Lesen doch sowieso nichts?

Wenn beide Threads nur lesend zugreifen, dann passiert IMHO nichts. Problematisch ist es wenn egal welcher Thread schreibend zugreift, dann geht von einem anderen Thread auch das Lesen in die Hose.

Ergo Lesen und Schreiben absichern.


Alle Zeitangaben in WEZ +1. Es ist jetzt 22:35 Uhr.
Seite 1 von 4  1 23     Letzte » 

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