我创建了以下抽象代码,其中用户有2个按钮:
一个按钮正在开始某种过程。全局变量PleaseStop将告诉正在运行的进程它应该停止它的工作。
另一个按钮设置全局变量PleaseStop
,它将告诉程序停止。
-
var
PleaseStop: boolean;
IsRunning: boolean;
procedure TForm1.RunActionClick(Sender: TObject);
var
rnd: integer;
tic: Cardinal;
begin
try
IsRunning := true;
rnd := Random(100);
while not PleaseStop do
begin
tic := GetTickCount;
while (GetTickCount-tic < 1000) and not PleaseStop do
begin
Application.ProcessMessages;
Sleep(10);
end;
Memo1.Lines.Add(IntToStr(rnd));
end;
finally
IsRunning := false;
PleaseStop := false;
end;
end;
procedure TForm1.StopBtnClick(Sender: TObject);
begin
PleaseStop := true;
end;
一切都按预期工作。
如果用户没有单击“停止”按钮,而是再次单击“运行”按钮(应该允许),则会出现问题。
我现在修改了我的代码:
var
PleaseStop: boolean;
IsRunning: boolean;
procedure TForm1.Button1Click(Sender: TObject);
var
rnd: integer;
tic: Cardinal;
begin
// ---- BEGIN NEW ----
if IsRunning then
begin
PleaseStop := true; // End the previous actions
while PleaseStop do // Wait until the previous actions are done
begin
// TODO: this loop goes forever. PleaseStop will never become false
Application.ProcessMessages;
Sleep(10);
end;
// Now we can continue
end;
// ---- END NEW ----
try
IsRunning := true;
rnd := Random(100);
while not PleaseStop do
begin
tic := GetTickCount;
while (GetTickCount-tic < 1000) and not PleaseStop do
begin
Application.ProcessMessages;
Sleep(10);
end;
Memo1.Lines.Add(IntToStr(rnd));
end;
finally
IsRunning := false;
PleaseStop := false;
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
PleaseStop := true;
end;
再次单击“开始”按钮将导致死锁。
我假设编译器认为while PleaseStop do
等于while true do
,因为我之前只将PleaseStop
设置为true
。但事实上,应该监控这个变量......
我也尝试将[volatile]
放在变量前面,并使它们成为TForm1的成员,但这也不起作用。
我为什么不使用线程?
代码严重依赖VCL。
运行按钮将开始直播。每次单击运行按钮时,将选择随机图片文件夹。
因此,当用户不喜欢这些图片时,他会再次点击“运行”切换到新文件夹并自动开始新的直播。因此,之前的运行应该停止。
答案 0 :(得分:4)
您的诊断不准确,ProcessMessages
,简单地说,不能导致之前检索到的消息继续处理。您必须停止处理,并从重新发生的地方继续执行。重新入侵是Application.ProcessMessages
的主要回避原因,并且您是故意这样做的。很难解决......
如果您不想使用同步和线程,则可以使用计时器。代码也会简单得多。
procedure TForm1.FormCreate(Sender: TObject);
begin
Timer1.Enabled := False;
Randomize;
end;
var
rnd: Integer;
procedure TForm1.StartClick(Sender: TObject);
begin
rnd := Random(100);
Timer1.Enabled := True;
end;
procedure TForm1.StopClick(Sender: TObject);
begin
Timer1.Enabled := False;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
Memo1.Lines.Add(IntToStr(rnd));
end;
答案 1 :(得分:0)
尝试重写这样的事件处理程序:
var Needtorestart:Boolean = false;
procedure TForm1.Button1Click(Sender: TObject);
var
rnd: integer;
tic: Cardinal;
begin
// ---- BEGIN NEW ----
if IsRunning then
begin
PleaseStop := true; // End the previous action
Needtorestart := true;
Exit;
// Now we can continue
end;
// ---- END NEW ----
try
repeat
IsRunning := true;
rnd := Random(100);
while not PleaseStop do
begin
Tic := GetTickCount;
while (GetTickCount-tic < 1000) and not PleaseStop do
begin
Application.ProcessMessages;
Sleep(10);
end;
Memo1.Lines.Add(IntToStr(rnd));
end;
PleaseStop := false;
until not Needtorestart;
finally
IsRunning := false;
PleaseStop := false;
Needtorestart := false;
end;
end;