delphi multithreading
Propiedad Threadsafe genérica (1)
Creé esta propiedad genérica "threadsafe", que puedo usar entre el hilo principal y un hilo de fondo. Lo hice porque estaba cansado de crear Lock-Objects para todas mis propiedades y 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;
¿Hay algún problema que esté pasando por alto? Este es un código que usa esta clase de propiedad. Dime que piensas.
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;
Su código está bien y serializará el acceso de lectura / escritura a la propiedad. El único comentario que haría es que no es necesario crear un objeto de bloqueo por separado. Puede quitar PropLock
y bloquearlo en su lugar.
Tengo una clase casi idéntica en mi código base. Las únicas diferencias son:
- Uso una sección crítica en lugar de
TMonitor
porque todavía no confío enTMonitor
. Las primeras versiones tenían una serie de errores y eso abatió mi confianza. Sin embargo, sospecho que el código deTMonitor
es más probable ahora. Entonces no veo ninguna razón para que cambies. - Utilizo try / finally con el código que bloquea y desbloquea. Esto es quizás un poco pesimista de mi parte, porque es difícil ver cómo podría recuperarse de las excepciones en los métodos get y setter. Fuerza de hábito, supongo.
FWIW, mi versión de tu clase se ve así:
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;
¡Creo que solo hay una forma de escribir esta clase!