2017-01-14 3 views
-5

Как я могу заменить код noob на хороший цикл? У меня есть 32 таймера на form1, каждый таймер работает каждые 1 секунду и исполняет bat-файл, а затем ждет, пока файл bat закончит его работу, и таймер снова запустится.Создать TTimer в цикле

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; 
+0

все должны работать параллельно, конечно – waza123

+0

Изображение вашего кода абсолютно бесполезно здесь. См. [This Meta post] (http://meta.stackoverflow.com/a/285557/62576) для списка многих причин, по которым изображения кода неприемлемы. Кроме того, изображение формы тоже бесполезно; он не дает ничего полезного для вопроса (если вы просто не хотите показать, как аккуратно вы разместили все те компоненты TTimer). –

+3

Какую проблему вы пытаетесь решить. Это вряд ли будет решением. –

ответ

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. 
+2

Полезно, но есть некоторые проблемы, если кто-то захочет его повторно использовать. Возникающие проблемы объективно бедны, поскольку они не классифицируются как «предпочтение стиля», но код действительно работает: 1) Не используйте маскировку, скрытие и игнорирование исключений. Программирование страуса очень сложно поддерживать и поддерживать. 2) Обратите внимание, что используемые классы уже предоставляют и не переопределяют существующие функции: механизм останова-запроса дублирует «TThread.Terminate();» и «Terminated». 3) Связанный с 1 недостающий пакетный файл имеет поток, который тихо ничего не делает каждую секунду - ошибки должны быть сообщены пользователю каким-то образом. –

+0

4) Нет никакой выгоды в ограничении списка конкретным типом потока. После того, как поток добавлен в список, нет зависимости от 'TBatchExecutionThread'. Таким образом, можно также разрешить списку держать любой 'TThread'. –

+0

PS: Is 'FreeOnTerminate: = True;' отсутствует? –

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;