2013-03-25 17 views
6

デルファイのマルチスレッドに問題があります。私は名前のリスト(約2.000の名前)を持っているので、自分のサイトの各名前のデータを取得する必要があります。私のシステムはスレッド制御を除いて完全に動作します。マルチスレッドDelphi

私は10個のスレッドを作成したいと思います。スレッドが終了すると、リストの最後まで別の...を作成します。

var 
Form1: TForm; 
tCount: Integer; //threads count 

implementation 

type 
TCheck = class(TThread) 
public 
    constructor Create(Name: string); 
    destructor Destroy; Override; 
protected 
    procedure Execute; Override; 
end; 

MainT = class(TThread) 
protected 
    procedure Execute; Override; 
end; 

destructor TCheck.Destroy; 
begin 
Dec(tCount); 
end; 

procedure MainT.Execute; 
var 
i: Integer; 
Load: TStringList; 
begin 
Load:=TStringList.Create; 
Load.LoadFromFile('C:\mynames.txt'); 

for i:= 0 to Load.Count -1 do 
begin 

    if tCount = 10 then //if we have 10 threads running... 
    begin 
    repeat 
    Sleep(1); 
    until tCount < 10; 
    end; 

    TCheck.Create(Load.Strings[i]); 
    TCheck.Start; 
    Inc(tCount); 

end; 

end; // end of procedure 

問題は、私が作成したスレッドの数をチェックしてるかの方法であるので、まあ、私はTCheck.Constructorを入れていません。私のソフトウェアは、エラーメッセージが表示されずに停止することがあります。時には500の名前、時には150の名前をチェックすることもあります。

申し訳ありません。

+0

TCheckツールを投稿できますか? –

+5

また、10個のスレッドが必要な場合は、10個のスレッドを作成し、キューに入れてすべての作業を処理させます。それらを継続的に作成/終了/破棄しないでください。 tCountとマイクロ管理スレッドは忘れてください。 –

+2

うん。スレッドセーフ待ち行列を埋めるプロデューサーと、それを排除する消費者が必要です。 –

答えて

3

ですジェネリックを使用してスレッドセーフキューソリューション。

はどのように多くの消費者の目を定義します。キューの深さを確認し、スレッドからDoSomeJobプロシージャを実行するだけです。

CaptureJobで)一般的な手順として文字列を扱う仕事を定義します。

キューが空の場合、コンシューマスレッドは破棄されます。 DoSomeJobプロシージャは、すべてのジョブが準備完了するまで待機します。 これを汎用ワーカープールに簡単に変換し、スレッドを破棄せずに再利用することができます。職務項目の一般的な構造は、さまざまな種類の作業を処理するのにも適しています。

このキューはXE2以上で動作することに注意してください。古いDelphiバージョンを使用している場合は、コメントに示されているような類似のスレッドセーフキューを探します。

uses 
    Classes,SyncObjs,Generics.Collections; 

Type 
  TMyConsumerItem = class(TThread) 
  private 
    FQueue : TThreadedQueue<TProc>; 
    FSignal : TCountDownEvent; 
  protected 
    procedure Execute; override; 
  public 
    constructor Create(aQueue : TThreadedQueue<TProc>; aSignal : TCountdownEvent); 
  end; 

constructor TMyConsumerItem.Create(aQueue: TThreadedQueue<TProc>); 
begin 
    Inherited Create(false); 
    Self.FreeOnTerminate := true; 
    FQueue := aQueue; 
    FSignal := aSignal; 
end; 

procedure TMyConsumerItem.Execute; 
var 
    aProc : TProc; 
begin 
    try 
    repeat 
     FQueue.PopItem(aProc); 
     if not Assigned(aProc) then 
     break; // Drop this thread 
     aProc(); 
    until Terminated; 
    finally 
    FSignal.Signal; 
    end; 
end; 

procedure DoSomeJob(myListItems : TStringList); 
const 
    cThreadCount = 10; 
    cMyQueueDepth = 100; 
var 
    i : Integer; 
    aQueue : TThreadedQueue<TProc>; 
    aCounter : TCountDownEvent; 
    function CaptureJob(const aString : string) : TProc; 
    begin 
    Result := 
     procedure 
     begin 
     // Do some job with aString 
     end; 
    end; 
begin 
    aQueue := TThreadedQueue<TProc>.Create(cMyQueueDepth); 
    aCounter := TCountDownEvent.Create(cThreadCount); 
    try 
    for i := 1 to cThreadCount do 
     TMyConsumerItem.Create(aQueue,aCounter); 
    for i := 0 to myListItems.Count-1 do begin 
     aQueue.PushItem(CaptureJob(myListItems[i])); 
    end; 
    finally 
    for i := 1 to cThreadCount do 
     aQueue.PushItem(nil); 
    aCounter.WaitFor; // Wait for threads to finish 
    aCounter.Free; 
    aQueue.Free; 
    end; 
end; 

NB:あなたの初期化とスレッドの開始が間違っている理由ケンは説明しています。この提案は、より一般的な方法でこのタイプの問題を処理するためのより良い構造を示しています。

+0

これは、質問のコードがどのように機能しないかを説明するものではありません。 Delphiのどのバージョンが使用されているのかについての元の質問には何も示されていないので、選択肢は一般的ではありません。 (上記の私の答えの最後の2つの段落を参照してください):-)(downvoting - コメントを残してください) –

+1

@KenWhite、この答えは、この問題がより良い構造でどのように解決できるかを示すことでした。ポスターコードのエラーを発見するためのすべてのクレジット –

1

あなたがTCheck.Createの戻り値を保持する変数を宣言しない場合、あなたは(あなたがStartメソッドにアクセスするために使用することができますTCheckのインスタンスがありません)TCheck.Startにアクセスすることはできません。あなたがそれを使用する必要はありませんので、

Check := TCheck.Create(Load[i]); { See note below } 
Check.Start; 
Inc(tCount); 

NOTE TStringListのデフォルトプロパティがStringsある

適切な方法は、var Check: TCheck;MainT.Execute内を宣言した後、返された値を格納するだろう。あなたは上記のように直接Stringsにアクセスすることができます。次の2行はまったく同じものです(ただし、明らかに1を入力して短く、簡単です):あなたはTCheckへの参照を保持したくない場合は、単にwithブロックであるためにあなたのコードを変更

Load.Strings[i]; 
Load[i]; 

(これはある(begin..endを含めて、ブロック内の他のコードを含まない唯一の方法は私が今まで使用してお勧めしますwith):それと

with TCheck.Create(Load[i]) do 
begin 
    Start; 
    Inc(tCount); 
end; 

は、あなたがこれを行うことができますより良い方法がある、言われていますすべての種類のthを作成/破壊する代わりに読む。他の人が言ったように、あなたは10スレッドのリストを持っていて、それらのために作業をキューイングできるので、それぞれがLoadのアイテムを処理し、完了すると別のアイテムを処理し、リストが完了するまで繰り返すことができます。それはあなたのDelphiのバージョンに依存するので、あなたがそれを行う方法を正確に言うのは難しいです。 (非常に役立つかもしれないいくつかの他のタイプと機能性がありOMNIThreadLibraryのように、あなたのためのほとんどの作業を行います使用可能なライブラリですが、それは、Delphiの一部の古いバージョンでは利用できないのです。デルファイの最近のバージョンもTQueueTObjectQueueサポートしています。ここで

(あなたがスレッドの限られた数のキューにこれを行う方法についてのさまざまな質問がある場合は、それが新しい質問、あなたはこの1つに追加しないものでなければなりません。)