Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - Frage zu threadsicheren Parameterübergabe


Erichgue - Do 22.03.12 09:31
Titel: Frage zu threadsicheren Parameterübergabe
Hallo Zusammen!

Folgende Frage beschäftigt mich?
Die Funktion "MachWas" sollte eigendlich Threadsicher sein.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
Function TTest.MachWas(Value : Integer) : Integer;
begin
  // 1
  EnterCriticalSection(fLock); 
  // 2
  Result := Value + Value;      
  // 3
  LeaveCriticalSection(fLock);
end;



Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
Procedure TThread1.Execute;
begin
  While not Terminated do
  begin
    fTest.Machwas(10);
  end
end



Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
Procedure TThread2.Execute;
begin
  While not Terminated do
  begin
    fTest.Machwas(20);
  end
end


Beide Threads werde gestartet.
Thread1 ruft "Machwas" mit dem Wert 10 auf und steht vor //2
Thread2 ruft "MachWas" mit dem Wert 20 auf und wird durch "EnterCriticalSection" in Machwas erstmal daran gehindert weiter zu machen.
Thread1 wird jetzt weiter ausgeführt. Welchen Wert hat jetzt Value?
Da Thread2 ja bereits in die Function reinkam und seinen Wert in Value übergeben hat.
Rechnet jetzt "MachWas" mit dem Wert 10 von thread1 oder mit dem Wert 20 von thread2?

Vielen Dank!

Erich


Moderiert von user profile iconNarses: Topic aus Sonstiges (Delphi) verschoben am Do 22.03.2012 um 21:45


bummi - Do 22.03.12 09:38

Das ist ja eine Simple Funtion wie Sin(), ABS() etc. hier gibt es keinerlei Variablen die durch Locking gesichert werden müssten.
Eine halbwegs aktuelle Diskussion zu diesem Thema lief unter http://www.delphipraxis.net/167197-aenderungen-von-variablen-synchronisieren.html


Erichgue - Do 22.03.12 09:44

Hallo bummi,
es geht je nicht um das was die Funktion macht, sonder um das Prinzip.
Der Link von dir brachte mir schon etwas mehr klarheit.
Um sicher zu gehen, das die Funktion nicht den Übergabeparameter von Thread 2 verwendet,
sollte man die Werte nach EnterCriticalSection zwischen speichern.

Danke!


bummi - Do 22.03.12 10:10

Die Werte sind auf dem persönlichen Stck des Threads, warum und wohin willst Du etwas zwischenspeichern.
Du könntest ja keine einzige Funktion/Prozedur aus einem Thread heraus aufrufen, wenn es so kompliziert wäre wie Du vermutest.
Die Lockings benötigst Du nur wenn verschiedene Threads auf dieselben Variablen zugreifen müssen.


jaenicke - Do 22.03.12 10:24

Also:
Du hast eine Klasse, in der steht wie diese aussehen soll. Dann wird ein konkreter Thread erstellt, eine Instanz der Klasse, mit der dann gearbeitet wird.

Jetzt stell dir das ganze am besten so vor:
Ein Architekt hat einen Bauplan (=Klasse) für ein Einfamilienhaus. Nun werden davon zwei gebaut (=Instanzen erstellt). Wenn nun jemand in einem der Häuser etwas aus dem Kühlschrank nimmt, verschwindet es nicht auch in dem anderen. :zwinker:

Solange du also nicht gemeinsame Variablen hast und veränderst (z.B. die selbe Straße vor zwei benachbarten Häusern aufgerissen werden soll), muss also auch keine Synchronisation passieren (in Form von Absperrungen auf der Straße z.B.).


Erichgue - Do 22.03.12 11:19

Vielen Dank für eure Beiträge!
Ein Test hat bei mir genau das ergeben, was ihr gschrieben habt.
Jetzt kann ich wieder ruhig schlafen.

Danke!

Erich


delfiphan - Do 22.03.12 20:58

Hmm.. Mit mehreren Instanzen einer Klasse hat das eigentlich nichts zu tun. Es geht eher darum, dass bei der Übergabe des Arguments jeweils eine lokale Kopie gemacht wird (man spricht auch von "by value").

Diese Kopie wird bei jedem Aufruf gemacht. Wenn jetzt die Methode auf zwei Threads gleichzeitig ausgeführt wird, dann arbeiten die mit zwei verschiedenen Kopien, die sich nicht beeinflussen. Auch wenn du innerhalb deiner Funktion die Funktion nochmals rekursiv aufrufen würdest, wären das jeweils immer separate Kopien.

Das heisst: Der Parameter "Value" kann auf Thread 1 den Wert 10 haben und auf Thread 2 gleichzeitig den Wert 20 und (bei Rekursionen) auch auf dem Thread 2 den Wert 30 haben. Es ist nicht so, dass "Value" immer nur einen Wert aufs Mal haben kann. Da ist wohl das Missverständnis.

Wie bummi bereits gesagt hat, sind lokale Variablen und Argumente standardmässig auf dem Stack (es gibt Ausnahmen z.B. Closures) oder in einem Register und jeder Thread hat seinen eigenen Stack (und Register). Es ist von einem Thread aus nicht einfach möglich, Werte im Stack eines anderen Threads zu lesen (es sei denn du arbeitest mit Pointern oder machst sonstigen Schwachsinn).

Bei globalen Variablen und Objektdaten sieht das anders aus. Diese sind in einem anderen Bereich des Speichers und "gehören" keinem speziellen Thread oder Aufruf (letzteres auf dem Heap und ersteres sonst irgendwo). Da muss man jeweils aufpassen, dass nicht zwei Threads gleichzeitig unkontrolliert darauf zugreifen.

Deine CritialSections kannst du also weglassen, denn da kommen nur lokale Variablen und Parameter vor, deren Werte bei jedem Aufruf (auch bei Rekursionen im gleichen Thread) auf frische Speicherbereiche gelegt werden.


Erichgue - Fr 30.03.12 16:33

Danke.
Diese Erklärung hat mir noch gefehlt!