Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic Threadsafe Property

I have created this "threadsafe" generic property, that i can use between the main thread and a background Thread. I made it because i was tired of creating Lock-Objects for all my properties and variables.

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;

Are there any problems i am overlooking? This is some code using this property class. Tell me what you think.

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;
like image 966
r_j Avatar asked Oct 31 '13 09:10

r_j


People also ask

Is Java file thread-safe?

6) Atomic operations in Java are thread-safe like reading a 32-bit int from memory because it's an atomic operation it can't interleave with other threads.

How do you make a class thread-safe in C#?

In order to make a thread as thread safe there are some thread synchronization techniques like Monitor/Lock, Mutex, Semaphore and SemaphoreSlim using these techniques we can achieve Thread Safety.

What means thread-safe?

In computer programming, thread-safe describes a program portion or routine that can be called from multiple programming threads without unwanted interaction between the threads. (A thread is an instance of the program running on behalf of some user or process.)


1 Answers

Your code is fine and will serialize read/write access to the property. The only comment that I would make is that you do not need to create a separate lock object. You can remove PropLock and lock on Self instead.

I have an almost identical class in my code base. The only differences are:

  1. I use a critical section rather than TMonitor because I still don't trust TMonitor. The early versions had a number of bugs and that dented my confidence. However, I suspect that the TMonitor code is most likely correct by now. So I see no reason for you to change.
  2. I use try/finally with the code that locks and unlocks. This is perhaps a little pessimistic on my part, because it's hard to see how you could usefully recover from exceptions in the getter and setter methods. Force of habit I suppose.

FWIW, my version of your class looks like this:

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;

I guess there's really just one way to write this class!

like image 103
David Heffernan Avatar answered Oct 23 '22 04:10

David Heffernan