Delphi: voorkeurswijze van bescherming met behulp van kritieke secties

Ik heb een object x dat moet wofden benaderd vanuit verschillende (5+ threads). De structuur van het object is

    Tx = class
    private
      Fx: integer;
    public
      property x: integer read Fx read Fx;
   etc;

Wat is de betere (meest elegante) manier van bescherming:

een)

Tx = class
 private
   Fx: integer;
 public
   property x: integer read Fx read Fx;
 public
   constructof Create; <--- Create the criticalsection here
   destructof Destroy; <--destroy it here
 etc;

var
   cs: TCriticalSection;
   Obj: Tx;

function GetSafeObject(): Tx;
begin
CS.Enter;
try
Result:= Obj;
finally
CS.Leave;

einde;

en benader altijd het object als GetSafeObj (). x: = 3;

of

Tx = class
 private
   Fx: integer;
   FCS: TCriticalSection;
 public
   property x: integer read GerX read SetX;
 public
   constructof Create; <--- Create the criticalsection here
   destructof Destroy; <--destroy it here
 etc;

where 
 function Tx.Getx(): integer;
 begin
   CS.Enter;
   try
     Result:= Fx;
   finally
     CS.Leave;
   einde;
einde;

einde;

and always access the object nofmally. I guess the first option is mofe elegant, even if both methods should wofk fine. Ay comments?

8
Je vraag is slecht gesteld. U moet beschrijven hoe u van plan bent om toegang te krijgen tot het gehele getal x
toegevoegd de auteur David Heffernan, de bron
Ik zou geen van deze twee opties gebruiken, maar vertrouwen op de methoden Vergrendelen/ontgrendelen (of de kritieke sectie openbaar maken), zoals voorgesteld in het antwoord van TOndrej. Optie A zou niets beschermen. Optie B werkt met geheel getal, maar zal mislukken als u een klasseninstantie retourneert: als u een kopie maakt van de eigenschapswaarde (dit is het geval met een geheel getal of zelfs tekenreeks ), dit is OK, maar u moet alle geretourneerde gegevens beveiligen als deze door middel van verwijzing wordt geretourneerd.
toegevoegd de auteur Arnaud Bouchez, de bron

3 antwoord

Ga voor optie B en maak de kritieke sectie intern aan het object. Als de gebruiker van de klas gebruik moet maken van een externe functie om veilig toegang te krijgen tot de instantie, is het onvermijdelijk dat iemand dit niet doet en het huis naar beneden valt.

You also need to think about what operational semantic you are wanting to protect from multiple concurrent reads & writes. If you put a lock inside your getter and setter, you can guarantee that your object is internally coherent, but users of your object may see multithreading artifacts. For example, if thread A writes 10 to a property of your object, and thread B writes 50 to that property of the same object, only one of them can be last one in. If A happens to go first, then A will observe that they wrote a 10 to the property, but when they read it back again they see B's 50 that snuck in there in the gap between A's read-after-write.

Merk ook op dat je niet echt een vergrendeling nodig hebt om een ​​enkel integerveld te beschermen. Aligned pointer sized integer writes zijn atomaire bewerkingen op zowat elk hardwaresysteem van tegenwoordig. Je hebt zeker een slot nodig om multi-piece data zoals structs of multi-step operaties te beschermen, zoals het tegelijkertijd veranderen van twee gerelateerde velden.

Als er een manier is waarop je je ontwerp kunt herwerken om deze objecten lokaal te maken voor een bepaalde bewerking op een thread, doe het dan. Lokale kopieën van gegevens maken, kan uw geheugenvoetafdruk enigszins vergroten, maar het kan uw code voor multithreading aanzienlijk vereenvoudigen en sneller werken dan het verlaten van mutex-landmijnen overal in de app. Kijk ook naar andere, vereenvoudigende aannames: als u uw systeem zo kunt instellen dat het object onveranderbaar is terwijl het beschikbaar is voor meerdere threads, heeft het object helemaal geen vergrendelingsbeveiliging nodig. Alleen-lezen gegevens zijn goed voor het delen van threads. Heel heel goed.

7
toegevoegd
Atomic write betekent thread safe om ervoor te zorgen dat geen enkele lezer ooit een gedeeltelijk geschreven waarde kan lezen. Atomic write is zinloos als u multivalue-semantiek hebt (meerdere stukjes gegevens die in dezelfde bewerking moeten worden bijgewerkt) en uw ontwerp vereist dat alle waarden te allen tijde coherent met elkaar zijn.
toegevoegd de auteur dthorpe, de bron
Als de bron die moet worden beschermd het gehele veld is, kan een interne vergrendeling gerechtvaardigd zijn - of zou volledig onnodig kunnen zijn. Als de bron die moet worden beschermd (moet worden bewaard in een consistente staat) het volledige object is, is een check-outmodel om exclusieve toegang tot de instantie te verkrijgen (zoals TThreadList) meer geschikt. PS, ik schreef TThreadList. ;>
toegevoegd de auteur dthorpe, de bron
@ArnaudBouchez Het OP vroeg naar de veiligheid van threads met een integer-veld. Als ze in plaats daarvan een complex/meerwaardig type gebruiken, zoals klasse in plaats van een geheel getalveld, dan hebben ze de verkeerde vraag gesteld.
toegevoegd de auteur dthorpe, de bron
Atomic-bediening is niet gelijk aan thread-safe. Afhankelijk van de implementatie. Lees meer hier .
toegevoegd de auteur LU RD, de bron
Dat is de reden waarom de Lock/UnLock-methoden zinvol zijn - en waarom het antwoord van TOndrej mij beter klinkt.
toegevoegd de auteur Arnaud Bouchez, de bron

Het CS maken van een lid van het object en het gebruiken van de CS in eigenschap-getter/settermethoden is de juiste benadering. De andere benadering werkt niet omdat deze de CS vergrendelt en ontgrendelt voordat het object daadwerkelijk wordt bewerkt, zodat de eigenschapswaarde helemaal niet wordt beschermd.

6
toegevoegd

Een eenvoudige manier is om een ​​threadveilige verpakking om het object te hebben, vergelijkbaar met TThreadList . De verpakking heeft twee methoden nodig: Vergrendelen (om de kritieke sectie in te voeren en het binnenste object te retourneren) en Ontgrendelen (om de kritieke sectie te verlaten).

5
toegevoegd
+1 Ook al maken dergelijke methoden de gebruikerscode een beetje uitgebreider ( Vergrendelen, proberen ... eindelijk Ontgrendelen; einde; ), dit is de beste optie voor prestaties (vooral als je object toegankelijk is) veilig vóór multi-threading, of als u toegang wilt hebben tot verschillende eigenschappen van de objecten), en ook voor race-voorwaarde-identificatie met complexe typen (werken met geheel getal is OK, maar als de eigenschap een klasse-instantie retourneert, is het hele proces zou beter worden beschermd totdat je expliciet het object niet meer gebruikt).
toegevoegd de auteur Arnaud Bouchez, de bron