简体   繁体   English

Delphi中的线程安全

[英]Thread-safe in delphi

I have to modify and change some visual components in a thread and as you know it's not safe to doing this. 我必须修改和更改线程中的某些可视组件,并且您知道这样做不安全。

My question is how to write a completely thread-safe code? 我的问题是如何编写完全线程安全的代码? It is possible? 有可能的? if it is then can you please give me a simple example? 如果可以,请给我一个简单的例子吗?

my code that is not 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;

Thank you. 谢谢。

Writing a thread safe code in Delphi involves the basic care you would have in any other language, which means to deal with race conditions . 用Delphi编写线程安全代码涉及您使用任何其他语言所需要的基本护理,即处理竞争条件 A race condition happens when different threads access the same data . 当不同的线程访问相同的数据时,就会发生竞争状态。 A good way to deal with that is to declare an instance of TCriticalSection and wrap the dangerous code in it. 处理该问题的一种好方法是声明TCriticalSection的实例,然后将危险代码包装在其中。

The code below shows a getter and a setter of a property that, by hypotesis, has a race condition. 下面的代码显示了一个属性的获取器和设置器,通过假设,该属性具有竞争条件。

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;

Notice the use of a single instance of TCriticalSection ( CriticalX ) to serialize the access to the data member FX . 注意,使用TCriticalSectionCriticalX )的单个实例来序列化对数据成员FX的访问。

However, with Delphi you have an aditional consideration! 但是,在使用Delphi时,您还有其他考虑! VCL is not thread safe, so in order to avoid VCL race conditions, any operation that results in screen changing must run in the main thread. VCL并不是线程安全的,因此为了避免VCL争用情况,导致屏幕切换的任何操作都必须在主线程中运行。 You get that by calling such a code inside a Synchronize method. 您可以通过在Synchronize方法中调用此类代码来实现。 Considering the class above, you should do something like this: 考虑到上面的类,您应该执行以下操作:

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

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

If you have Delphi 2010 or later, there is an easier way that makes use of anonymous methods: 如果您使用的是Delphi 2010或更高版本,则可以使用一种更简单的方法来使用匿名方法:

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

I hope this helps! 我希望这有帮助!

You should only access VCL objects from main VCL thread. 您只能从主VCL线程访问VCL对象。

Some reading methods (property getters) do work from other threads in practice - but you have to prove it in advance reading VCL sources for the specific Delphi build. 实际上,某些读取方法(属性获取器)可以在其他线程上工作-但是您必须事先为特定的Delphi构建读取VCL源代码进行证明。 Or not use it. 还是不使用它。

PS: Synchronize method runs given procedure in main VCL thread, pausing the caller thread, which may lead to a deadlock, if main thread was also blocked. PS:同步方法在主VCL线程中运行给定过程,暂停调用者线程,如果主线程也被阻塞,则可能导致死锁。

Read more: (actually making this answer to list some links) 阅读更多:(实际上是回答这个问题以列出一些链接)

My problem solved with Synchronize! 我的问题通过Synchronize!解决了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;

Thank you all for helping me. 谢谢大家的帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM