2017-01-14 12 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

からコードを使用すると、バックグラウンドで長時間の操作を実行したいので、ここに行くための方法ではありませんです。バッチファイルを実行すると、バッチファイルに時間がかかる場合、現在の方法でバッチファイルを実行するとユーザーがブロックされます。

より良いアプローチ:明示的なスレッドを使用する。 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: 'FreeOnTerminate:= True;'がありませんか? –

1

受け入れ答えはすでにタイマーがあなたの問題を解決されないことを概説し、あなたの代わりにスレッドを使用する必要があります。しかし、あなたが投稿したコードには、より根本的な問題があると思います。 のための短いDRY

ソフトウェア工学の一つの重要な原則が呼び出されるは、自分でを繰り返さないでください。あなたが何度も何度も何度も同じことをしていると、間違っている可能性があります。しかし、32個のタイマーを個別に作成するのではなく、ループを使用するように提案して、すでに正しい方向に進んでいます。

タイマーごとに別々の手順を書いて、同じコードを非常に小さな違いで繰り返しているようです。代わりに、プロシージャを1回だけ記述し、それをすべてのタイマーに割り当てたいとします。唯一行う必要のある調整は、コードからのファイル名の数を決定することです。

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; 

ファイル名を変数と同じように2回作成するのではなく、変数に保存することに注意してください。それは同じ原理です。あなたは二度物事をしたくないのです。ファイルの名前の変更方法を考えてみましょう。元のバージョンでは64箇所(各タイマーごとに2回)にコードを調整しなければならないため、1回だけ調整する必要があります。

同じように、すべてのタイマーをコードからループで作成できます。これはおそらくあなたの問題のための良い解決策ではありませんが、それほど良いことではありません。

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; 
関連する問題