9

据我了解并知道 TThread 类的方法,如果您同步代码,它实际上会在主应用程序线程中执行(就像计时器/按钮单击/等)我一直在玩耍并注意到一个MessageBox 不会阻塞主应用程序,但是 sleep 会按预期进行。这是为什么?

type
  TTestThread = class(TThread)
  private
    procedure SynchThread;
  protected
    procedure Execute; override;
  public
    constructor Create(CreateSuspended: Boolean);
  end;

procedure TTestThread.SynchThread;
begin
 MessageBoxA (0, 'Hello', 'Test', 0);
end;

procedure TTestThread.Execute;
begin
 Synchronize (SynchThread)
end;

constructor TTestThread.Create(CreateSuspended: Boolean);
begin
  inherited;
  FreeOnTerminate := True;
end;

procedure StartThread;
var
 TestThread : TTestThread;
begin
 TestThread := TTestThread.Create (FALSE);
end;
4

3 回答 3

13

这个答案有两个部分。

第 1 部分在如果 MessageBox()/related 是同步的,为什么我的消息循环不冻结?. MessageBox 函数不是阻塞的,它只是创建一个带有自己的消息循环的对话框。

第 2 部分在MessageBox 文档中进行了解释。

hWnd:要创建的消息框的所有者窗口的句柄。如果此参数为 NULL,则消息框没有所有者窗口。

当您显示模态对话框时,Windows 会禁用它的所有者,但如果您为第一个参数传递 0,则没有所有者并且没有可禁用的内容。因此,您的程序将在显示消息框时继续处理消息(并对它们作出反应)。

要更改此行为,请将表单的句柄作为第一个参数传递。例如:

procedure TTestThread.SynchThread;
begin
  MessageBoxA (Form1.Handle, 'Hello', 'Test', 0);
end;
于 2013-03-29T07:31:05.117 回答
12

我怀疑这个问题归结为你说的意思:

消息框不会阻止主应用程序。

我的意思是当你显示消息框时,你的 VCL 表单仍然可以交互。这里的问题与线程无关,我建议我们将它们从等式中删除。你对做什么的理解Synchronize是正确的。

该问题完全与窗口所有者的概念以及模态对话框窗口相对于其所有者的行为方式有关。请注意,所有者不是指 Delphi 属性TComponent.Owner,而是指 Windows API 的owner含义。

创建一个 VCL 应用程序并在表单上放置两个按钮。添加以下OnClick处理程序。

procedure TForm1.Button1Click(Sender: TObject);
begin
  MessageBox(0, 'Not owned', nil, MB_OK);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  MessageBox(Handle, 'Owned by the VCL form', nil, MB_OK);
end;

现在观察单击 时会发生什么Button1。消息框显示,但您仍然可以单击 VCL 表单。并与 比较Button2。当它显示消息框时,无法与 VCL 表单交互。

当显示模式对话框窗口时,对话框窗口将禁用其所有者。在 的情况下Button2,所有者是 VCL 表单。一旦表单被禁用,您就无法与之交互。在 的情况下Button1,没有所有者,因此模态对话框窗口不会禁用任何其他窗口。这就是为什么可以与 VCL 表单交互的原因。

Raymond Chen 在他的 Old New Thing 博客上有一个关于模态的长系列:

于 2013-03-29T09:00:08.083 回答
4

同步将执行主线程中的代码。
一个很好的解释可以在这里找到Delphi TThread 类中的同步

您只需要阻止用户与您的应用程序的表单进行交互,例如。经过

procedure TTestThread.SynchThread;
begin
MessageBoxA (0, 'Hello', 'Test', MB_TASKMODAL);      
end;

像您一样使用 MessageBoxA 不会阻止主线程对由用户与您的表单交互触发的事件做出反应,只需尝试

procedure TForm4.Button2Click(Sender: TObject);
begin
    MessageBoxA (0, 'Hello', 'Test', 0);
   // vs
   //  MessageBoxA (0, 'Hello', 'Test', MB_TASKMODAL);
end;

消息框A

同步将在主线程中执行可以显示(恕我直言)

type
  TTestThread = class(TThread)
  private
    FSync:Boolean;
    FCalled:TDateTime;
    procedure SynchThread;
  protected
    procedure Execute; override;
  public
    constructor Create(CreateSuspended: Boolean;sync:Boolean);
  end;

procedure TTestThread.SynchThread;
begin
 MessageBox (0,PChar(DateTimeToStr(FCalled)+#13#10+DateTimeToStr(Now)),'Hello' , 0);
end;

procedure TTestThread.Execute;
begin
 sleep(100); // give Caller Time to fell asleep
 if Fsync then Synchronize (SynchThread) else SynchThread;
end;

constructor TTestThread.Create(CreateSuspended: Boolean;sync:Boolean);
begin
  inherited Create(CreateSuspended);
  FSync := Sync;
  FCalled :=Now;
  FreeOnTerminate := True;
end;

procedure StartThread(sync:Boolean);
var
 TestThread : TTestThread;
begin
 TestThread := TTestThread.Create (FALSE,sync);
end;

procedure TForm4.RunUnsynchronizedClick(Sender: TObject);
begin
   StartThread(false);// no sync
   Sleep(5000);       // Stop messageloop
end;

procedure TForm4.RunSynchronizedClick(Sender: TObject);
begin
   StartThread(true); // sync
   Sleep(5000);       // Stop messageloop
end;
于 2013-03-29T07:03:38.283 回答