AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Threadsicherheit

Ein Thema von Desmulator · begonnen am 26. Mai 2011 · letzter Beitrag vom 27. Mai 2011
Antwort Antwort
Benutzerbild von Desmulator
Desmulator

Registriert seit: 3. Mai 2007
Ort: Bonn
169 Beiträge
 
#1

Threadsicherheit

  Alt 26. Mai 2011, 22:22
Guten Abend,
in letzter Zeit befasse ich mich deutlich mehr mit Assembler und begebe mich, wie der Titel vielleicht schon verrät, in das Gebiet der Threads, sehr spannend, vorallem in Assembler.

Threads benutzen ist eine Sache, sie sicher zu benutzen eine andere. Während ich also arbeitet stellten sich mir einige Fragen, die eigentlich aus einem recht simplen Kontext entstanden.

Nehmen wir einfach mal das Beispiel für einen Referenzzähler, so wie TInterfacedObject es für uns macht. Ich kann mich vage erinnern, dass die Release-Methode ungefähr so aussah:

Delphi-Quellcode:
procedure TInterfacedObject._Release();
begin
  InterlockedDecrement(refcount);
  if refcount = 0 then Free();
end;
Sinngemäß sollte es das sein, analog dazu die AddRef-Methode:

Delphi-Quellcode:
procedure TInterfacedObject._AddRef();
begin
  InterlockedIncrement(refcount);
end;
Wahrscheinlich hat sich schon einiges geändert oder stimmt so nicht, doch ich glaube das trifft das Ganze schon ziemlich genau. Nun Sinn der Nutzung von InterlockedIncrement resp. InterlockedDecrement ist in meinen Augen natürlich die Threadsicherheit. Zwischen dem Lesen, Manipulieren und Schreiben des Wertes kann kein andere Thread auf diesen zugreifen, bzw. anders gesagt, es wird als eine Operation angesehen ( atomic-operation ), in Assambler dem lock-Prefix entsprechend.

Nun stellt sich mir aber die Frage, ist das wirklich threadsicher? Folgendes Beispiel:

Code:
Thread1 ruft _Release() auf.
  Der Zähler wird gesenkt:
    InterlockedDecrement(refcount);
  Der Wert wird geladen und verglichen, entsprechend folgt eine Sprungoperation oder nicht, schließlich der Call
    if refcount = 0 then Free();
  Kontextwechsel beim aufruf von Free. Ich meine mich zu erinnern irgendwo mal gelesen zu haben, dass hauptsächlich nach jumps, calls, rets o.Ä. ein solcher Hardwarekontextwechsel passiert.
Thread2 ruft _AddRef() auf.
  Der Zähler wird erhöht.
    InterlockedIncrement(refcount);
  ...
  Erneuter Kontextwechsel.
Thread1 führt nun fleißig Free aus. Die Daten sind nicht mehr im Speicher, Thread2 ist sich aber sicher über seinen Besitz des Objektes.

Unaufhaltsam rast Thread2 nun in eine Zugriffsverletzung.
Nun, das ist für mich alles andere als sicher. Darum kam mir der Gedanke, benutzen wir doch einfach mal kritische Abschnitte. Packen wir außen rum und alles ist super. Naja ~

Die CriticalSection will auch initialisiert und finalisiert werden, wie fast alles andere am Computer. Folgendes Szenario: Unser erster Thread betritt nun diese kritische Sektion, für alle anderen ist es gesperrt, er ist nun in der Lage das Objekt und die damit verbundene kritische Sektion wieder zu finalisieren, was ja auch richtig ist, wenn das Objekt gelöscht wird, ist für die kritische Sektion kein bedarf mehr. Aber was ist mit unserem zweiten Thread, der nun vielleicht schon darauf wartet die kritische Sektion zu betreten ( ich rede jetzt von EnterCriticalSection z.b. ).

MSDN kommentiert so:
Zitat:
If a critical section is deleted while it is still owned, the state of the threads waiting for ownership of the deleted critical section is undefined.
Nun ein undefinierter Status ist mir sehr unsympatisch. Frage ist jetzt, was soll getan werden?
Ich kann natürlich so argumentieren, dass ich sage, es ist ein Designfehler meines Programms, wenn so etwas passieren kann, doch ich denke, das ganze lässt sich auch auf andere Themen übertragen.

Schönen Abend noch ;D
Lars
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.184 Beiträge
 
Delphi 12 Athens
 
#2

AW: Threadsicherheit

  Alt 26. Mai 2011, 22:55
Du kannst nicht "treadsicher" direkt auf diese Variable zugreifen.

Delphi-Quellcode:
procedure TInterfacedObject._Release();
var
  i: Integer;
begin
  i := InterlockedDecrement(refcount);
  if i = 0 then Destroy;
end;
Theoretisch würde jetzt dennoch knallen, wenn zur selben Zeit _AddRef und _Release aufgerufen wird, wenn das InterlockedIncrement kurz nach InterlockedDecrement aufgerufen würde, da das Objekt freigegeben, aber praktisch sollte sowas nie passieren.
Denn wurde die letzte Referenz freigegeben, kann diese Variable keine Referenz mehr abgeben und somit kann sowas nie passieren.
Solange keiner an der Referenzzählung rumspielt und nirgendwo eine "Referenz" existiert, welcher nicht in der Referenzzählung enthalten ist.
$2B or not $2B
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.475 Beiträge
 
Delphi 12 Athens
 
#3

AW: Threadsicherheit

  Alt 27. Mai 2011, 08:32
Solange keiner an der Referenzzählung rumspielt und nirgendwo eine "Referenz" existiert, welcher nicht in der Referenzzählung enthalten ist.
In dem Fall hätte man auch in einer Single-Thread-Anwendung Probleme.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 04:16 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