![]() |
Ä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:
Solange der Wert innerhalb des Threads durch die Variable ThreadString_Locked gesperrt ist, kann der Variablen außerhalb des Threads nichts zugewiesen werden.
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; Ist dieser Ansatz sinnvoll bzw., gibt es eine einfachere oder besser Lösung? Gruß |
AW: Änderungen von Variablen synchronisieren
|
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 ^^ |
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:
statt
LOCK MOV [EAX], EDX
Delphi-Quellcode:
.
MOV [EAX], EDX
Und die WinAPI kennt die Interlocked-Behle, wie ![]() 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:
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.
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; Ich hatte letzens die CriticalSection über TryEnter und eine Schleife mit einem TimeOut versehn. Schön ist das nicht unbedingt, aber es funktioniert wenigstens. |
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:
Bin gerade über TMultiReadExclusiveWriteSynchronizer gestolpert, was haltet ihr davon? Ich muss den Wert vorwiegend lesen. |
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:
Bei
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;
Delphi-Quellcode:
wird solange gewartet, bis die CriticalSection frei ist und dann betreten.
FCS.Enter
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:
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.
FIntStr
Delphi-Quellcode:
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.
procedure TMyThread.Execute;
begin while not Terminated do begin ... // FIntStr := 'So nicht'; IntStr := 'So geht das'; ... end; end; end; |
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:
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. |
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. |
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:
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.
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; 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? |
AW: Änderungen von Variablen synchronisieren
Zitat:
Ergo Lesen und Schreiben absichern. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 11:03 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 by Thomas Breitkreuz