2013-10-31 18 views
10

Ich habe diese "threadsafe" generische Eigenschaft erstellt, die ich zwischen dem Hauptthread und einem Hintergrundthread verwenden kann. Ich habe es geschafft, weil ich es leid war, Lock-Objekte für all meine Eigenschaften und Variablen zu erstellen.Generic ThreadSafe Eigenschaft

TLockedProp<MyType> = class 
private 
    FMyProp:MyType; 
    PropLock:TObject; 
    procedure SetMyProp(const Value: MyType); 
    function GetMyProp: MyType; 
published 
    property Value:MyType read GetMyProp write SetMyProp; 
public 
    Constructor Create; 
    Destructor Destroy;override; 
end; 

{ TLockedProp<MyType> } 

constructor TLockedProp<MyType>.Create; 
begin 
    inherited; 
    PropLock:=TObject.create 
end; 

destructor TLockedProp<MyType>.Destroy; 
begin 
    PropLock.Free; 
    inherited; 
end; 

function TLockedProp<MyType>.GetMyProp: MyType; 
begin 
    TMonitor.Enter(PropLock); 
    result := FMyProp; 
    TMonitor.Exit(PropLock); 
end; 

procedure TLockedProp<MyType>.SetMyProp(const Value: MyType); 
begin 
    TMonitor.Enter(PropLock); 
    FMyProp := Value; 
    TMonitor.Exit(PropLock); 
end; 

Gibt es irgendwelche Probleme, die ich übersehe? Dies ist ein Code, der diese Eigenschaftenklasse verwendet. Sag mir was du denkst.

TBgThread=class(TThread) 
    private  
    FPaused: TLockedProp<boolean>; 
    FCount:TLockedProp<integer>; 

    procedure ChangeCount(pPlusMin:integer); 
    function GetPaused:boolean; 
    function GetCount:integer; 
    public 
    constructor Create; 
    destructor Destroy;override; 
    {Toggle Pause} 
    procedure PausePlay; 
    protected 
    procedure Execute;override; 
    published 
    Property Paused:boolean read GetPaused; 
    Property Count:integer read GetCount; 
    end; 
constructor TBgThread.Create(); 
begin 
    inherited Create(true);; 
    FPaused:=TLockedProp<boolean>.create; 
    FPaused.Value:=false;  
    FCount:=TLockedProp<integer>.create; 
    FCount.Value:=0; 
end; 
destructor TBgThread.Destroy; 
begin 
    FPaused.Free; 
    FCount.free;  
    inherited; 
end; 
procedure TBgThread.Execute; 
begin 
    inherited; 
    Repeat 
    if not Paused then begin 
     Try 
      //do something 
     finally 
      ChangeCount(+1); 
     end; 
    end else 
     Sleep(90); 
    Until Terminated; 
end; 

function TBgThread.GetCount: integer; 
begin 
    Result:=FCount.Value; 
end; 

procedure TBgThread.ChangeCount(pPlusMin: integer); 
begin 
    FCount.Value:=FCount.Value+pPlusMin; 
end; 

function TBgThread.GetPaused: boolean; 
begin 
    result := FPaused.Value; 
end; 

procedure TBgThread.PausePlay; 
begin 
    FPaused.Value:=not FPaused.Value; 
end; 

Antwort

15

Ihr Code ist in Ordnung und wird den Lese-/Schreibzugriff auf die Eigenschaft serialisieren. Der einzige Kommentar, den ich machen würde, ist, dass Sie kein separates Sperrobjekt erstellen müssen. Sie können PropLock entfernen und stattdessen Self sperren.

Ich habe eine fast identische Klasse in meiner Code-Basis. Die einzigen Unterschiede sind:

  1. Ich benutze einen kritischen Abschnitt anstatt TMonitor, weil ich nicht noch TMonitor vertrauen. Die frühen Versionen hatten eine Reihe von Bugs und das hat mein Selbstvertrauen beeinträchtigt. Ich vermute jedoch, dass der Code TMonitor höchstwahrscheinlich inzwischen korrekt ist. Also sehe ich keinen Grund für dich, dich zu ändern.
  2. Ich benutze try/schließlich mit dem Code, der sperrt und entsperrt. Dies ist vielleicht ein wenig pessimistisch, da es schwierig ist zu erkennen, wie Sie die Exceptions in den Getter- und Setter-Methoden sinnvollerweise wiederherstellen können. Kraft der Gewohnheit, nehme ich an.

FWIW, meine Version der Klasse sieht wie folgt aus:

type 
    TThreadsafe<T> = class 
    private 
    FLock: TCriticalSection; 
    FValue: T; 
    function GetValue: T; 
    procedure SetValue(const NewValue: T); 
    public 
    constructor Create; 
    destructor Destroy; override; 
    property Value: T read GetValue write SetValue; 
    end; 

{ TThreadsafe<T> } 

constructor TThreadsafe<T>.Create; 
begin 
    inherited; 
    FLock := TCriticalSection.Create; 
end; 

destructor TThreadsafe<T>.Destroy; 
begin 
    FLock.Free; 
    inherited; 
end; 

function TThreadsafe<T>.GetValue: T; 
begin 
    FLock.Acquire; 
    Try 
    Result := FValue; 
    Finally 
    FLock.Release; 
    End; 
end; 

procedure TThreadsafe<T>.SetValue(const NewValue: T); 
begin 
    FLock.Acquire; 
    Try 
    FValue := NewValue; 
    Finally 
    FLock.Release; 
    End; 
end; 

Ich denke, es ist wirklich nur eine Möglichkeit, diese Klasse zu schreiben!

+1

Thnx für Ihre Antwort, und ich schätze die Tatsache, dass Sie Ihre Version der Klasse freigegeben haben. Dadurch fühle ich mich als Programmierer sicherer, dass ich diese Lösung selbst entwickeln konnte. nur 1 Jahr Delphi unter meinem Gürtel;). –

+0

Beachten Sie, dass ein kritischer Abschnitt Leistungsprobleme haben kann, wenn die Klasse, die seinen Puffer enthält, kleiner als die CPU-Cache-Zeile ist. Siehe https://www.delphitools.info/2011/11/30/fixing-tcriticalsection/ Es kann besser/sicherer sein, einen kleinen Ausrichtungspuffer zur Klassendefinition hinzuzufügen und sich auf den kritischen Abschnitt des Betriebssystems anstelle der TCriticalSection-Klasse zu verlassen . –