Multithreading – common threadsafe properties
I created this "threadsafe" general property, which I can use between the main thread and the background thread I do this because I'm tired of creating lock objects for all 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;
What's the problem with my overlooking? This is some code that uses 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;
Solution
Your code is good and will serialize read / write access to this property My only comment is that you do not need to create a separate locked object You can remove proplock and lock self
There are almost the same classes in my code base The only difference is:
>I use key parts instead of tmmonitor because I still don't trust tmmonitor The early version had some mistakes and implied my confidence However, I suspect that the tmmonitor code is probably correct now So I can't see why you change. > I use try / finally with lock and unlock code This may be a bit pessimistic for me because it's hard to see how you can effectively recover from exceptions in getter and setter methods Suppose my habit
Fwiw, my 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 think there is really only one way to write this course!