7

我必须修改和更改线程中的一些可视化组件,如您所知,这样做是不安全的。

我的问题是如何编写一个完全线程安全的代码?有可能的?如果是的话,你能给我一个简单的例子吗?

我的代码不是线程安全的:

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;

谢谢你。

4

3 回答 3

10

在 Delphi 中编写线程安全代码涉及到您在任何其他语言中都会有的基本注意事项,这意味着处理竞争条件。当不同的线程访问相同的数据时,就会出现竞争条件。处理这个问题的一个好方法是声明一个TCriticalSection的实例并将危险代码包装在其中。

下面的代码显示了一个属性的 getter 和 setter,根据假设,该属性具有竞争条件。

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,您还有一个额外的考虑!VCL 不是线程安全的,因此为了避免 VCL 竞争条件,任何导致屏幕更改的操作都必须在主线程中运行。您可以通过在Synchronize方法中调用这样的代码来获得它。考虑到上面的类,你应该这样做:

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;

我希望这有帮助!

于 2013-07-17T18:00:39.557 回答
5

您应该只从主 VCL 线程访问 VCL 对象。

一些阅读方法(属性获取器)在实践中确实可以从其他线程工作 - 但您必须提前阅读特定 Delphi 构建的 VCL 源代码来证明这一点。或者不使用它。

PS:Synchronize方法在VCL主线程中运行给定程序,暂停调用者线程,如果主线程也被阻塞,可能会导致死锁。

阅读更多:(实际上使这个答案列出了一些链接)

于 2013-07-17T17:15:19.767 回答
0

我的问题解决了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;

谢谢大家帮助我。

于 2013-07-18T20:18:53.043 回答