在Thread中使用EnterCriticalSection来更新VCL标签

时间:2010-03-15 17:39:16

标签: delphi synchronization thread-safety vcl

我是线程的新手。我正在使用第三方库,它使用的线程有时会调用我提供的程序。

当线程调用时,如何从我的过程更新TLabel.Caption?

如果我在其他地方调用了InitializeCriticalSection,它就像

一样简单
  EnterCriticalSection(CritSect);
  GlobalVariable := 'New TLabel.Caption';
  LeaveCriticalSection(CritSect);

然后在我的主线程中:

  EnterCriticalSection(CritSect);
    Label1.Caption:= GlobalVariable;
  LeaveCriticalSection(CritSect);

但是,如何获取要调用的主线程代码?线程可以使用SendMessage吗?或者是否有一些更好/更简单的方法(.OnIdle可以检查线程设置的标志?)

感谢。

4 个答案:

答案 0 :(得分:5)

要在主线程中调用代码,请查看TThread.Synchronize。它接受一个方法指针(或者,在D2009 +中,一个匿名方法)并负责处理幕后的所有消息,以确保你的代码在主线程中运行。

答案 1 :(得分:5)

Critical Sections用于序列化对一段代码的访问。要更新图形用户界面,您应该注意只有主线程才能更新GUI元素。

因此,如果您的线程需要更新GUI元素,它应该将其委托给主线程。为此,您可以使用不同的技术:

最简单的方法是在线程代码中使用Synchronize方法。调用Synchronize时,您的线程暂停,您提供给Synchronize的代码将在主线程的上下文中执行,然后您的线程将恢复。

如果您不喜欢每次调用该代码时都停止线程,那么您可以使用Queue方法。队列将您的请求发送到目标线程的消息队列(此处为主线程),因此您的线程不会停止,但UI可能不会立即更新,具体取决于主线程消息队列的拥挤程度。

实现此目的的另一种方法是使用SendMessage或PostMessage API函数将自定义Windows消息发送到主线程。在这种情况下,您必须定义自定义消息,并在需要更改UI元素时将其发送到主线程。您的主线程应该为该类型的消息提供消息处理程序,并处理收到的消息。结果与使用Queue方法类似。

答案 2 :(得分:4)

您必须确保以安全的方式更新标签。此外,VCL在应用程序主线程中运行,并且从其他线程中弄乱它可能会产生奇怪的结果。即使使用关键部分。

所以我的建议是:只使用PostMessage。调用回调过程时,只需从该过程调用PostMessage到主窗体窗口句柄。这将确保标签标题在主线程的上下文中设置。

代码示例:

type
  TForm1 = class(TForm)
  private
    procedure OnWMUpdateLabel(var Msg: TMessage); message WM_UPDATE_LABEL;
    procedure MyCallbackProcedure(const Sender: TObject);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.OnWMUpdateLabel(var Msg: TMessage);
begin
  Label1.Caption := SomeVariable;
end;

procedure TForm1.MyCallbackProcedure(const Sender: TObject);
begin
  SomeVariable := 'New Label';
  PostMessage(Handle, WM_UPDATE_LABEL, 0, 0);
end;

但是如果你以这种方式传递字符串,你必须要小心。您必须同步对此类变量的访问权限。或者您可以使用GlobalAddAtom(有点弃用)或类似的东西。

编辑:

正如Mason已经说过你也可以使用更容易使用的Synchronize。对于你的问题,它应该是完全可以的。

编辑2:

参考如何使用GlobalAddAtom(是的,我之前拼写错误):

http://www.delphi3000.com/articles/article_574.asp?SK=

答案 3 :(得分:0)

我这样做的方法是让主应用程序线程在表单上使用TTimer来检查线程特定值的状态,以查看状态是否已更改,如果是,则更新标签(或其他组件) 。 TThread后代使用具有Getter功能的属性来保护关键部分的访问。这样,工作线程永远不会被主线程阻止(Synchronize将会这样做),并且用户不会在使用UI时遇到任何延迟。