据我了解并了解TThread类的方法,如果你同步你的代码,它实际上是在主应用程序线程中执行的(就像一个计时器/按钮点击等)。 我一直在玩,并注意到MessageBox不会阻止主应用程序,但是睡眠就像预期的那样。那是为什么?
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;
答案 0 :(得分:13)
这个答案有两个部分。
第1部分在If MessageBox()/related are synchronous, why doesn't my message loop freeze?中得到了很好的解释。 MessageBox函数没有阻塞,它只是创建一个带有自己的消息循环的对话框。
第2部分在MessageBox documentation中解释。
hWnd:要创建的消息框的所有者窗口的句柄。 如果这样 参数为NULL,消息框没有所有者窗口。
当您显示模式对话框时,Windows会禁用其所有者,但如果您为第一个参数传递0,则没有所有者,也无需禁用。因此,您的程序将在显示消息框时继续处理消息(并对其作出反应)。
要更改此行为,请将表单的句柄作为第一个参数传递。例如:
procedure TTestThread.SynchThread;
begin
MessageBoxA (Form1.Handle, 'Hello', 'Test', 0);
end;
答案 1 :(得分:12)
我怀疑这个问题归结为你说的意思:
消息框不会阻止主应用程序。
我认为这意味着当您显示消息框时,您的VCL表单仍然可以与之交互。这里的问题与线程无关,我建议我们将它们从等式中删除。您对Synchronize
的作用的理解是合理的。
这个问题与window's owner的概念完全相关,以及模态对话框窗口对其所有者的行为方式。请注意,所有者,我不是指Delphi属性TComponent.Owner
,但我的意思是{API {3}}的Windows API含义。
创建一个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博客上有一个关于模态的长篇系列:
答案 2 :(得分:4)
Synchronize将执行Mainthread中的代码 可以在Synchronization in Delphi TThread class
找到一个很好的解释您只需要阻止用户与您的应用程序表单进行交互,例如。由
procedure TTestThread.SynchThread;
begin
MessageBoxA (0, 'Hello', 'Test', MB_TASKMODAL);
end;
使用MessageBoxA,不会阻止Mainthread对ueser与表单交互触发的事件作出反应,只需尝试
procedure TForm4.Button2Click(Sender: TObject);
begin
MessageBoxA (0, 'Hello', 'Test', 0);
// vs
// MessageBoxA (0, 'Hello', 'Test', MB_TASKMODAL);
end;
同步将在主线程中执行,可以通过
显示(恕我直言)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;