Потокобезопасный в delphi

Я должен изменить и изменить некоторые визуальные компоненты в потоке, и, как вы знаете,это небезопасно.

У меня вопрос как написать полностью потокобезопасный код? это возможно? если да, то не могли бы вы привести простой пример?

мой код, который не является threadsafe:

type
  tMyWorkerThread = class(TThread)
      public
         procedure Execute; override;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure tMyWorkerThread.Execute;
begin
  //codes
  //working with visual components
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  TMyWorkerThread.Create(false);
end;

спасибо.

3 ответов


написание потокобезопасного кода в Delphi включает в себя основную заботу, которую вы имели бы на любом другом языке, что означает иметь дело с гонки. Условие гонки происходит, когда разные потоки обращаются к те же данные. Хороший способ справиться с этим-объявить экземпляр TCriticalSection и обернуть опасно код в нем.

код ниже показывает геттер и сеттер свойства, которое, по гипотезе, имеет состязание.

constructor TMyThread.Create;
begin
  CriticalX := TCriticalSection.Create;
end;

destructor TMyThread.Destroy; override;
begin
  FreeAndNil(CriticalX);
end;

function TMyThread.GetX: string;
begin
  CriticalX.Enter;
  try
    Result := FX;
  finally
    CriticalX.Leave;
  end;
end;

procedure TMyThread.SetX(const value: string);
begin
  CriticalX.Enter;
  try
    FX := Value;
  finally
    CriticalX.Leave;
  end;
end;

обратите внимание на использование одного экземпляра TCriticalSection (CriticalX) для сериализации доступа к элементу данных FX.

однако, с Delphi у вас есть aditional рассмотрение! VCL не является потокобезопасным, поэтому во избежание условий гонки VCL любая операция, которая приводит к экран изменение должен работать в основном потоке. Вы, что, называя такой код внутри синхронизация метод. Учитывая класс выше, вы должны сделать что-то вроде этого:

procedure TMyThread.ShowX;
begin
  Synchronize(SyncShowX);
end;

procedure TMyThread.SyncShowX;
begin
  ShowMessage(IntToStr(FX));
end;

Если у вас Delphi 2010 или позже, есть более простой способ, который использует анонимные методы:

procedure TMyThread.ShowX;
begin
  Synchronize(procedure begin
    ShowMessage(IntToStr(FX));
  end);
end;

надеюсь, это поможет!


вы должны получить доступ только к объектам VCL из основного потока VCL.

некоторые методы чтения (геттеры свойств) работают из других потоков на практике, но вы должны доказать это заранее, читая источники VCL для конкретной сборки Delphi. Или не использовать.

PS: метод Synchronize запускает данную процедуру в основном потоке VCL, приостанавливая поток вызывающего абонента, что может привести к взаимоблокировке, если основной поток также был заблокирован.

Read more: (фактически делая этот ответ список некоторых ссылок)


моя проблема решена с Synchronize!

type
  tMyWorkerThread = class(TThread)
      public
         procedure Execute; override;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure tMyWorkerThread.Execute;
begin

  //codes that takes long time
  Synchronize(procedure begin
     //working with visual components
  end
  );

end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  TMyWorkerThread.Create(false);
end;

спасибо всем за помощь мне.