等待全局变量更改其值

时间:2017-08-06 20:15:55

标签: delphi

我创建了以下抽象代码,其中用户有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。

  • 运行按钮将开始直播。每次单击运行按钮时,将选择随机图片文件夹。

  • 因此,当用户不喜欢这些图片时,他会再次点击“运行”切换到新文件夹并自动开始新的直播。因此,之前的运行应该停止。

2 个答案:

答案 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;