How to avoid GUI freeze when calling Beginthread API?
Below just a simple race between 2 button in 2 threads,
and this will freeze other compo开发者_开发知识库nents on the form.
procedure moveButton1();
var
I: Integer;
begin
for I := 0 to 6000 do
Form1.Button1.Left := Form1.Button1.Left - 1;
Form1.Caption := 'Button1 won!';
EndThread(0);
end;
procedure moveButton2();
var
I: Integer;
begin
for I := 0 to 6000 do
Form1.Button2.Left := Form1.Button2.Left - 1;
Form1.Caption := 'Button2 won!';
EndThread(0);
end;
procedure TForm1.Button3Click(Sender: TObject);
var
thread1, thread2,tick : Integer;
id1, id2 : LongWord;
begin
thread1 := BeginThread(nil,
0,
Addr(moveButton1),
nil,
0,
id1);
thread2 := BeginThread(nil,
0,
Addr(moveButton2),
nil,
0,
id2);
CloseHandle(thread1);
CloseHandle(thread2);
end;
The VCL (and parts of the RTL) is not thread-safe, so you cannot move components around from a thread. You have several options:
- Use a
TTimer
component. You don't need a thread and the timer's event handler will be executed in the context of the main thread. The timer was designed exactly for things like that. - Synchronize all VCL related stuff in the thread.
TThread
provides a static methodSynchronize
that does exactly that. - Send messages from the thread to the form using
SendMessage
orPostMessage
and handle this message in the form.
You might also consider to use the TThread
wrapper class instead of using BeginThread
and EndThread
explicitly when working with threads.
Using Synchronize() would be the down and dirty method of synchronizing the procedures that move the buttons. Synchronize() forces the method to be run in the main VCL thread. They'll block each other, so only one button can move at a time. This will avoid encountering non-thread safe code in the VCL.
I couldn't recreate the issue of the form freezing though, so I'm not sure that is your issue. You may wish to look elsewhere.
精彩评论