6

delphiのマルチスレッドで問題が発生しています。名前のリスト(約2.000の名前)があり、サイトで各名前のデータを取得する必要があります。私のシステムは、スレッド制御を除いて完全に機能します。

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の名前をチェックすることもあります...

悪い英語でごめんなさい。

4

2 に答える 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;

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

于 2013-03-25T21:49:05.527 に答える
1

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

var Check: TCheck;適切な方法は、内部を宣言してからMainT.Execute、返された値を格納することです。

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

のデフォルトプロパティTStringListStringsであるため、使用する必要はありません。Strings上記のように直接アクセスできます。次の2行はまったく同じです(ただし、明らかに1つは短く、入力が簡単です)。

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

への参照を保持したくない場合はTCheck、コードをブロックに変更するだけwithです(を含み、ブロックbegin..endに他のコードが含まれていません(これは私がこれまでに使用することをお勧めする唯一の方法withです):

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

そうは言っても、あらゆる種類のスレッドを作成/破棄する代わりに、これを行うためのはるかに優れた方法があります。他の人が言っているように、10個のスレッドのリストを作成し、それらの作業をキューに入れて、それぞれがLoadアイテムを処理し、完了したら別のアイテムを処理するために戻って、リストが完了するまで繰り返すことができます。Delphiのバージョンによって異なるため、これをどのように行うかを正確に言うのは困難です。(のように、ほとんどの作業を実行するライブラリが利用可能ですがOMNIThreadLibrary、一部の古いバージョンのDelphiでは利用できません。最近のバージョンのDelphiは、およびその他の非常に役立つタイプと機能もサポートTQueueしています。TObjectQueue

(スレッド数が制限されているキューでこれを行う方法について別の質問がある場合は、これに追加するものではなく、新しい質問にする必要があります。)

于 2013-03-25T19:04:56.857 に答える