thread multithreading delphi generics thread-safety delphi-xe5

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:

  1. Uso una sección crítica en lugar de TMonitor porque todavía no confío en TMonitor . Las primeras versiones tenían una serie de errores y eso abatió mi confianza. Sin embargo, sospecho que el código de TMonitor es más probable ahora. Entonces no veo ninguna razón para que cambies.
  2. 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!