2

Delphi 使用: 2007

こんにちは、みんな、

ViewStyleに設定された TListView がありvsReportます。ボタンをクリックすると、約 50 のスレッドが起動します。各スレッドにはTListItemコンポーネントがあります。それぞれTListItemSubItemタイマーである があります。250 から始まり、0 まで下がります。ユーザーは、TListView で各タイマーの減少を確認できます。

私は次のコードを書きました:

procedure TThreadWorker.DeleteTickets;
begin
 ListItem.Delete;
end;

procedure TThreadWorker.UpdateTimer;
begin
 ListItem.SubItems[1] := IntToStr(Timer);
end;

procedure TThreadWorker.TimerCounter;
begin
 Timer := 300;
 repeat
  Sleep(1000);
  Dec(Timer);
  Synchronize(UpdateTimer);
 until (Timer = 0);
 Synchronize(DeleteTickets);
end;

そして...それは動作します!しかし、問題は次のとおりです。これらの同期はすべて、不必要に CPU を過負荷にしているようです。明らかに、より多くのスレッド (100、200、または 300) を起動したり、より弱いコンピューターを使用したりすると、より大きな問題になります。最初は、それが同期であるかどうかわかりませんでした。しかし、それらを無効にすると、CPU が過負荷になることはありません。

率直に言って、それはそれほど問題ではありません。しかし、タイマーの減分によって CPU の過負荷が発生するべきではないと感じています。私のコードはおそらく正しくありません。呼び出しUpdateTimer頻度を減らしてみたところ、CPU の過負荷は緩和されましたが、最終的には修正されませんでした。さらに、タイマーが毎秒更新されるのをユーザーに見てもらいたいと思います。タイマーも可能な限り正確である必要があります。

ありがとうございました。

4

3 に答える 3

3

ここで馬車を馬の前に置いたと思います。すべてのスレッドをメイン スレッドに同期させ、それぞれに独自のタイマーとメッセージ キューを持たせると、システムに大きな負担がかかります。さらに、多くの場合、メッセージ ループを実行してスレッドに負荷をかけたくない場合があります。

私の見解では、より良いアプローチは、メインスレッドに単一のタイマーを配置することです。カチカチ音をたてたら、報告する必要がある各スレッドまたはタスクから進行状況を取得します。その進行状況へのアクセスをシリアル化する必要がありますが、それは高価ではありません。

于 2013-03-17T13:42:13.110 に答える
1

スレッドはあなたが思っているよりもCPUにとって高価だと思います。私が覚えていることから、CPUには、キャッシュに出入りする各スレッドをスワップするオーバーヘッドがあります。CPUが50の異なるスレッドをスワップアウトしているので、私はその過負荷に驚いていません

1つの解決策は、TTimerコンポーネントを拡張してから、50スレッドではなく50スレッドを動的に作成することです。TTimerは、スレッドの代わりにWindowsAPIを使用します。(以下のコードはテストされていませんが、少なくともあなたにアイデアを与えるはずです)

TMyTimer = class(TTimer)
begin
  public
    Timer: integer;
    ListItem: TListItem;
end;
...
procedure ButtonClick(Sender: TObject)
begin
  for i := 0 to 50 do
  begin
     ltimer = TMyTimer.Create;
     ltimer.Timer := 300;
     ltimer.ListItem := TListItem.Create;
     //initialize list item here
     ltimer.OnTimer := DecTimer;
  end;
end;

procedure DecTimer(Sender: TObject)
begin
  dec(TMytimer(Sender).Timer);
  TMyTimer(Sender).ListItem.SubItem[1] := StrToInt(TMytimer(Sender).Timer)
end;

スレッドがすべて同時に開始している場合は、1つのスレッドで最大25のタイマーを制御するなどの方法を試してください。つまり、50タイマーの場合、スレッドは2つだけです。タイマーイベントは、25個のカウンターをループして、それらをデクリメントします。これには、同期を使用する必要があります。

この質問への答えは興味深いかもしれません: スレッドはどれくらい高価ですか?

于 2013-03-17T05:05:33.187 に答える
1

TThread.Queueこれは、の代わりにとを使用しTSimpleEventてカウンターのタイミングを調整する例Sleep()です。

Type
  TThreadWorker = Class(TThread)
  private
    FTimer : Integer;
    FListItem : TListItem;
    procedure Execute; override;
    procedure UpdateTimer;
    procedure DeleteTicket;
  public
    constructor Create( aListItem : TListItem);
  End;

constructor TThreadWorker.Create(aListItem : TListItem);
begin
  Inherited Create(false);
  FListItem := aListItem;
  Self.FreeOnTerminate := true;
end;

procedure TThreadWorker.Execute;
var
  anEvent : TSimpleEvent;
begin
  anEvent := TSimpleEvent.Create(nil,true,false,'');
  try
    FTimer := 300;
    repeat
      anEvent.WaitFor(1000);
      Queue(UpdateTimer);
      Dec(FTimer);
    until (FTimer = 0);
    Self.Synchronize( DeleteTicket); // <-- Do not Queue this !
  finally
    anEvent.Free;
  end;
end;

procedure TThreadWorker.UpdateTimer;
begin
  FListItem.SubItems[1] := IntToStr(FTimer);
end;

procedure TThreadWorker.DeleteTicket;
begin
  FListItem.Delete;
end;

注意点として、DeleteTicketは同期する必要があります。実行が完了するとスレッドは終了し、キュー上のすべてのものがぶら下がったままになります。

于 2013-03-17T13:20:10.597 に答える