delphi中的线程安全

Posted

技术标签:

【中文标题】delphi中的线程安全【英文标题】:Thread-safe in delphi 【发布时间】:2013-07-17 16:22:02 【问题描述】:

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

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

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

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;

谢谢。

【问题讨论】:

看看Synchronize 使用File->New->Other 菜单,选择Delphi Projects->Delphi Files->Thread Object,然后阅读它创建的新单元顶部的巨大评论 我必须修改和更改一些虚拟组件 虚拟组件还是可视组件?以及什么样的更新?进度条可以很容易地从主线程完成,而工作线程只会报告他们的工作 - 只需几行代码即可完成 Update a VCL component from CreateAnonymousThread的可能重复 【参考方案1】:

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

下面的代码显示了一个属性的 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;

我希望这会有所帮助!

【讨论】:

您能否停止调用 critical section s - 甚至是另一篇帖子中的 event s - Mutex?如果您知道 Mutex 是什么,这可能会造成混淆,如果您不知道,则无法帮助您理解您的代码。【参考方案2】:

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

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

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

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

http://www.michael-puff.de/Programmierung/Delphi/Code-Snippets/VCLThreadDemo.shtml http://www.drbob42.com/uk-bug/hood-04.htm http://delphi.about.com/od/kbthread/a/thread-gui.htm Is it better to use TThread's "Synchronize" or use Window Messages for IPC between main and child thread? Delphi 6 : breakpoint triggered on non-VCL thread stops main thread repaints http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/devwin32/win32_mthreadusemainthread_xml.html Simplifying VCL thread wrapper code Update a VCL component from CreateAnonymousThread http://thaddy.co.uk/threads/ - Martin Harvey 的“多线程 - 德尔福之道”的镜子 http://otl.17slon.com/ - 新的 Delphi 线程处理方法

【讨论】:

好清单! Martin Harvey 的“Multithreading - The Delphi Way”也应该在上面。在我看来,在顶部位置:-D 不幸的是,original 不再存在,但周围有几个“缓存”副本:Thaddy's 和 http://web.archive.org/ 是我所知道的。它也可以通过Code Central 获得。 @MarjanVenema 好的。您的链接非常有价值,因为实际上我的这个列表只需 5 分钟的谷歌搜索,而且您的链接需要先验知识。 如果 Martin 的精彩文章被 URL 烂掉了,我把它贴在我的网站上:seti.net/engineering/threads/threads.php 好吧,但是字典是 RTL。【参考方案3】:

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;

谢谢大家帮助我。

【讨论】:

很遗憾您会添加并接受自己的答案,而不是告诉您同样事情的其他答案之一。真的打破了 Stack Overflow 的工作流程。

以上是关于delphi中的线程安全的主要内容,如果未能解决你的问题,请参考以下文章

我可以假设 Delphi NOW 函数是线程安全的吗?

一个队列类的实现(比delphi自带的速度快70倍)(线程安全版本)

delphi VCL显示问题 分线程与主线程的同步

C/C++线程安全型队列的实现

Delphi中的整数读取是原子的吗?

Delphi 多线程问题