2013-10-31 78 views
10

我已經創建了這個「線程安全」通用屬性,可以在主線程和後臺線程之間使用。我做了它,因爲我厭倦了爲我所有的屬性和變量創建鎖定對象。通用線程安全屬性

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; 

有沒有我忽略的問題? 這是一些使用此屬性類的代碼。告訴我你的想法。

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; 

回答

15

您的代碼很好,並且會對該屬性進行序列化讀/寫訪問。我會做的唯一的評論是你不需要創建一個單獨的鎖對象。您可以刪除PropLock並鎖定Self

我在我的代碼庫中有一個幾乎相同的類。唯一的區別是:

  1. 我用一個關鍵部分,而不是TMonitor,因爲我還是不放心TMonitor。早期版本有一些錯誤,這削弱了我的信心。但是,我懷疑TMonitor代碼現在很可能是正確的。所以我看不出有什麼理由讓你改變。
  2. 我使用try/finally與代碼鎖定和解鎖。對我而言,這可能有些悲觀,因爲很難看到如何從getter和setter方法中有效地從異常中恢復過來。我想,習慣的力量。

FWIW,我對你的類的版本是這樣的:

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; 

我想有真的只是一個方式來寫這個類!

+1

Thnx爲你的答案,我真的很感謝你分享你的課堂版本。作爲一名程序員,讓我感到更加自信,因爲我能夠自己想出這個解決方案。只有一年的德爾福在我的腰帶下;)。 –

+0

請注意,如果保存其緩衝區的類比CPU緩存線更小,則關鍵部分可能會出現性能問題。請參見https://www.delphitools.info/2011/11/30/fixing-tcriticalsection/向類定義中添加小型對齊緩衝區可能會更好/更安全,並且依賴於OS臨界區而不是TCriticalSection類。 –