Потокобезопасный объект (критические секции)

Общие вопросы программирования, алгоритмы и т.п.

Модератор: Модераторы

Потокобезопасный объект (критические секции)

Сообщение Angel_19 » 12.04.2016 11:09:00

Пробую создавать потокобезопасный объект для хранения данных, чтобы к нему можно было обращаться из разных потоков.
За пример беру: TThreadList

Код: Выделить всё
Type
{ TData }
  TData = Class
    fData: Integer;
    FLock: TRTLCriticalSection;
  private
    function GetData: Integer;
    procedure SetData(AValue: Integer);
  public
    Procedure Lock;
    Procedure UnLock;
    Constructor Create;
    Destructor Destroy; override;
    Property Data: Integer read GetData write SetData;
  end;

implementation

{ TData }

function TData.GetData: Integer;
begin
    Lock;
    try
      Result := fData;
    finally
      UnLock;
    end;
end;

procedure TData.SetData(AValue: Integer);
begin
    Lock;
    try
      fData := AValue;
    finally
      UnLock;
    end;

end;

procedure TData.Lock;
begin
    System.EnterCriticalSection(FLock);
end;

procedure TData.UnLock;
begin
    System.LeaveCriticalSection(FLock);
end;

constructor TData.Create;
begin
    InitCriticalSection(FLock);
end;

destructor TData.Destroy;
begin
  inherited Destroy;
  DoneCriticalSection(FLock);
end;


В потоке вызываю так:

Код: Выделить всё
TInt:= TestD1.Data;
inc(TInt);
TestD1.Data:=TInt;


При этом после вычислений значение TestD1.Data отклоняется от ожидаемой величины.
Но если вызывать в потоке вот так:
Код: Выделить всё
    TestD1.Lock;
    try
      TInt:= TestD1.Data;
      inc(TInt);
      TestD1.Data:=TInt;
    finally
      TestD1.UnLock;
    end;

То все работает как нужно.

Что делаю не так?
Angel_19
новенький
 
Сообщения: 37
Зарегистрирован: 24.06.2014 17:29:47

Re: Потокобезопасный объект (критические секции)

Сообщение Дож » 12.04.2016 11:31:36

Ну и где код запуска потока? У меня всё работает:
Код: Выделить всё
{$MODE OBJFPC}
Type
{ TData }
  TData = Class
    fData: Integer;
    FLock: TRTLCriticalSection;
  private
    function GetData: Integer;
    procedure SetData(AValue: Integer);
  public
    Procedure Lock;
    Procedure UnLock;
    Constructor Create;
    Destructor Destroy; override;
    Property Data: Integer read GetData write SetData;
  end;

function TData.GetData: Integer;
begin
    Lock;
    try
      Result := fData;
    finally
      UnLock;
    end;
end;

procedure TData.SetData(AValue: Integer);
begin
    Lock;
    try
      fData := AValue;
    finally
      UnLock;
    end;

end;

procedure TData.Lock;
begin
    System.EnterCriticalSection(FLock);
end;

procedure TData.UnLock;
begin
    System.LeaveCriticalSection(FLock);
end;

constructor TData.Create;
begin
    InitCriticalSection(FLock);
end;

destructor TData.Destroy;
begin
  inherited Destroy;
  DoneCriticalSection(FLock);
end;

function ThreadFunc(Param: Pointer): PtrInt;
var
  TInt: Integer;
  TestD1: TData;
begin
  TestD1 := TData(Param);
  TInt:= TestD1.Data;
  inc(TInt);
  TestD1.Data:=TInt;
  Result := 0;
end;

var
  TestD1: TData;

begin
  TestD1 := TData.Create;
  TestD1.fData := 0;

  WaitForThreadTerminate(BeginThread(@ThreadFunc, Pointer(TestD1)), 5000);

  Writeln(TestD1.Data);
end.


Код: Выделить всё
C:\data\temp>fpc -gl td.pas && td.exe
Free Pascal Compiler version 3.0.0rc1 [2015/08/10] for i386
Copyright (c) 1993-2015 by Florian Klaempfl and others
Target OS: Win32 for i386
Compiling td.pas
Linking td.exe
82 lines compiled, 0.3 sec, 36384 bytes code, 1316 bytes data
1


Angel_19 писал(а):Но если вызывать в потоке вот так:
Код: Выделить всё
    TestD1.Lock;
    try
      TInt:= TestD1.Data;
      inc(TInt);
      TestD1.Data:=TInt;
    finally
      TestD1.UnLock;
    end;

То все работает как нужно.

Что делаю не так?

После сеанса телепатии предположу, что в этом варианте тред успевает залочить объект и основной тред не получает значение TestD1.Data, пока не произойдёт анлок. При этом в изначальном варианте у вас главный поток печатает сообщение сразу же, не дожидаясь выполнения треда.
Аватара пользователя
Дож
энтузиаст
 
Сообщения: 899
Зарегистрирован: 12.10.2008 16:14:47

Re: Потокобезопасный объект (критические секции)

Сообщение Angel_19 » 12.04.2016 11:35:25

Всем спасибо, я сам понял ошибку.

В первом варианте я блокирую доступ лишь на время получения значения и на время записи нового! Но нужно блокировать доступ на все время: пока получаю значение и пока записываю новое. Иначе в промежутках пока я вычисляю новое значение, другой поток может считать старое значение и увеличить уже его!
Angel_19
новенький
 
Сообщения: 37
Зарегистрирован: 24.06.2014 17:29:47


Вернуться в Общее

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 6

Рейтинг@Mail.ru