通用的线程安全属性

11

我创建了这个“线程安全”的通用属性,它可以在主线程和后台线程之间使用。我之所以创建它,是因为我厌倦了为我的所有属性和变量创建锁对象。

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;
1个回答

16

您的代码很好,可以序列化读/写访问属性。我唯一想要提出的评论是您不需要创建单独的锁对象。您可以删除PropLock并改为在Self上加锁。

我在我的代码库中有一个几乎相同的类。唯一的区别是:

  1. 我使用临界区而不是TMonitor,因为我仍然不信任TMonitor。早期版本存在许多错误,这损害了我的信心。但是,我认为TMonitor代码现在可能已经正确了。所以我认为您没有必要更改。
  2. 我在锁定和解锁的代码中使用try/finally。这可能有点悲观,因为很难看到您如何从getter和setter方法中有用地恢复异常。可能是习惯使然。

顺便说一下,我的版本如下:

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;

我想只有一种方法可以编写这个类!

2
谢谢你的回答,我真的很感激你分享了你的类版本。这让我作为一个程序员感到更有信心,因为我能够自己想出这个解决方案。只有一年的Delphi编程经验 ;)。 - r_j
请注意,如果持有缓冲区的类小于CPU缓存行,则关键部分可能存在性能问题。请参见https://www.delphitools.info/2011/11/30/fixing-tcriticalsection/。最好/更安全的做法是在类定义中添加一个小对齐缓冲区,并依赖于操作系统的关键部分,而不是TCriticalSection类。 - Arnaud Bouchez
你如何编写一个示例来展示如何使用这个类? - peiman F.
@peiman 有一个公共属性。有多少种可能的方法可以使用它? - David Heffernan
我还不理解这个概念,但我正在尝试通过一个例子来理解:)所以您的回答没有帮助到我 :| 我必须为每个共享变量创建一个TThreadsafe吗?! - peiman F.
是的,每个变量一个实例。 - David Heffernan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接