Общее свойство Threadsafe

Я создал это универсальное свойство "поточности", которое я могу использовать между основным потоком и фоновым потоком. Я сделал это, потому что устал создавать объекты блокировки для всех своих свойств и переменных.

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;

person r_j    schedule 31.10.2013    source источник


Ответы (1)


Ваш код в порядке и будет сериализовать доступ для чтения/записи к свойству. Единственное замечание, которое я хотел бы сделать, это то, что вам не нужно создавать отдельный объект блокировки. Вы можете удалить PropLock и вместо этого заблокировать Self.

У меня есть почти идентичный класс в моей кодовой базе. Единственные отличия:

  1. Я использую критическую секцию, а не TMonitor, потому что я все еще не доверяю TMonitor. В ранних версиях было несколько ошибок, и это подорвало мою уверенность. Однако я подозреваю, что код TMonitor сейчас, скорее всего, правильный. Поэтому я не вижу причин, чтобы вы изменились.
  2. Я использую try/finally с кодом, который блокирует и разблокирует. Это, возможно, немного пессимистично с моей стороны, потому что трудно понять, как можно с пользой восстановиться после исключений в методах получения и установки. Сила привычки, я полагаю.

FWIW, моя версия вашего класса выглядит так:

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;

Я думаю, есть только один способ написать этот класс!

person David Heffernan    schedule 31.10.2013
comment
Спасибо за ваш ответ, и я очень ценю тот факт, что вы поделились своей версией класса. Я как программист чувствую себя более уверенно, потому что смог сам придумать это решение. только 1 год delphi за плечами;). - person r_j; 31.10.2013
comment
Обратите внимание, что критический раздел может иметь проблемы с производительностью, если класс, содержащий его буфер, меньше, чем строка кэша ЦП. См. delphitools.info/2011/11/30/fixing-tcriticalsection Возможно, будет лучше/безопаснее добавить небольшой буфер выравнивания в определение класса и полагаться на критическую секцию ОС вместо класса TCriticalSection. - person Arnaud Bouchez; 17.11.2015
comment
как вы пишете пример, чтобы показать, как использовать этот класс? - person peiman F.; 08.07.2018
comment
@peiman есть одно общедоступное свойство. Сколько возможных способов может быть, чтобы использовать его - person David Heffernan; 08.07.2018
comment
я еще не понимаю концепцию, и я пытаюсь привести пример :), поэтому ваш ответ мне не помог: | я должен создать один TThreadsafe для каждой общей переменной?! - person peiman F.; 08.07.2018
comment
да. Один экземпляр на переменную - person David Heffernan; 08.07.2018