循环创建TTimer

时间:2017-01-14 22:09:16

标签: delphi

如何用漂亮的循环替换我的noob代码? 我在form1上有32个计时器,每个计时器每1秒运行一次并执行bat文件,然后等待bat文件完成他的工作并且计时器再次运行。

enter image description here

enter image description here

以下是图片

的代码
procedure TForm1.Timer1Timer(Sender: TObject);
var nr:string;
begin
  nr := '1';
  Timer1.Enabled := False;

  if g_stop=False then
  begin
    if FileExists('test'+nr+'.bat') then
    begin
      ExeAndWait(ExtractFilePath(Application.ExeName) + 'test'+nr+'.bat', SW_SHOWNORMAL);
    end;
    Timer1.Enabled := True;
  end;
end;



procedure TForm1.Timer2Timer(Sender: TObject);
var nr:string;
begin
  nr := '2';
  Timer2.Enabled := False;

  if g_stop=False then
  begin
    if FileExists('test'+nr+'.bat') then
    begin
      ExeAndWait(ExtractFilePath(Application.ExeName) + 'test'+nr+'.bat', SW_SHOWNORMAL);
    end;
    Timer2.Enabled := True;
  end;
end;




procedure TForm1.Timer3Timer(Sender: TObject);
var nr:string;
begin
  nr := '3';
  Timer3.Enabled := False;

  if g_stop=False then
  begin
    if FileExists('test'+nr+'.bat') then
    begin
      ExeAndWait(ExtractFilePath(Application.ExeName) + 'test'+nr+'.bat', SW_SHOWNORMAL);
    end;
    Timer3.Enabled := True;
  end;
end;

2 个答案:

答案 0 :(得分:1)

因为你想在后台运行冗长的操作,所以使用计时器不是这里的方法。如果批处理文件需要一些时间,那么当前运行批处理文件的方式将阻止用户ui。

更好的方法:使用显式线程。创建自己的线程类作为TThread的后代。您的类的每个实例都有一个特定的文件名,它将持续运行它。

unit BatchExecutionThread;

interface
uses Classes;

type

TBatchExecutionThread = class (TThread)
  private
    pBatchFileToExecute: string;
  public
    constructor Create(ABatchFileToExecute: string);

    procedure Execute; override;
end;

implementation

uses SysUtils, Windows;

constructor TBatchExecutionThread.Create(ABatchFileToExecute: string);
begin
  inherited Create;

  pBatchFileToExecute := ABatchFileToExecute;
end;

procedure TBatchExecutionThread.Execute;
begin
   { While no stop requested }
   while(not Terminated) do
   begin
      try
        { Execute Batch file if it exists }
        if FileExists(pBatchFileToExecute) then
        begin
          ExeAndWait(pBatchFileToExecute, SW_SHOWNORMAL);
        end;
      except
        { Ignore exception }
      end;
      { Wait a second }
      Sleep(1000);
   end;
end;

end.

然后,从您的应用程序中,您可以为要运行的每个文件为此线程类创建许多不同的实例。

例如:

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, BatchExecutionThread, System.Generics.Collections;

type
  TForm1 = class(TForm)
    ButtonStart: TButton;
    ButtonStop: TButton;
    procedure ButtonStartClick(Sender: TObject);
    procedure ButtonStopClick(Sender: TObject);
  private
    pThreads: TList<TBatchExecutionThread>;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.ButtonStartClick(Sender: TObject);
var
  i: Integer;
  FileName: string;
  Thread: TBatchExecutionThread;
begin
  if(pThreads = nil) then
  begin
    { Create a list where we store the running threads }
    pThreads := TList<TBatchExecutionThread>.Create;

    { Create 10 threads with the batch file names from 1 to 10 }
    for i:= 1 to 10 do
    begin
      { Build the filename }
      FileName := ExtractFilePath(Application.ExeName) + 'test'+ i.ToString() +'.bat';

      { Create a thread for this file }
      Thread :=  TBatchExecutionThread.Create(FileName);
      Thread.FreeOnTerminate := true;

      { Add the thread to the list }
      pThreads.Add(Thread);

      { Start the thread }
      Thread.Start();
    end;
  end;
  { else Already started }
end;

procedure TForm1.ButtonStopClick(Sender: TObject);
var
 Thread: TBatchExecutionThread;
begin
  if(pThreads <> nil) then
  begin
    { Tell all threads to stop }
    for Thread in pThreads do
    begin
      Thread.Terminate;
    end;

    { Delete list of threads }
    FreeAndNil(pThreads);
  end;
  { else not started yet }
end;

end.

答案 1 :(得分:1)

已接受的答案已经概述了定时器不是您问题的解决方案,您应该使用线程代替。但是,我认为您发布的代码存在更多基本问题。

软件工程的一个重要原则称为 DRY 的缩写<不要重复自己。每当你发现自己一遍又一遍地做同样的事情时,你很可能做错了。但是你已经走上正轨,建议使用循环而不是单独创建32个定时器。

看起来你为每个计时器编写了一个单独的程序,重复相同的代码,但差别很小。相反,您只想编写一次该过程,并将其分配给所有定时器。您必须进行的唯一调整是从代码中确定文件名的编号。

procedure TForm1.TimerTimer(Sender: TObject);
  var FileName: string;
begin
  FileName := ExtractFilePath(Application.ExeName) + 'test' + String(TTimer(Sender).Name).Replace('Timer', '') + '.bat';
  TTimer(Sender).Enabled := False;

  if (not g_stop) then
  begin
    if FileExists(FileName) then
    begin
      ExeAndWait(FileName, SW_SHOWNORMAL);
    end;
    TTimer(Sender).Enabled := True;
  end;
end;

请注意,我还将文件名保存到变量中,而不是像在代码中那样构造它两次。这是相同的原则 - 你不想做两次事情。想象一下,你想改变文件的命名方式 - 使用你的原始版本,你必须在64个地方调整你的代码(每个计时器两次),现在你只需要进行一次调整。

同样,您可以在代码循环中创建所有计时器。再次注意,这对你的问题可能不是一个好的解决方案,但这是一个很好的练习。

procedure TForm1.CreateTimers;
  var i: integer;
      Timer: TTimer;
begin
  for i := 1 to 32 do
  begin
    Timer := TTimer.Create(self);
    Timer.Interval := 1000;
    Timer.Name := 'Timer' + i.ToString;
    Timer.OnTimer := TimerTimer;
  end;
end;