如何用漂亮的循环替换我的noob代码? 我在form1上有32个计时器,每个计时器每1秒运行一次并执行bat文件,然后等待bat文件完成他的工作并且计时器再次运行。
以下是图片
的代码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;
答案 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;