4

Delphiプログラムには、長くて単純なループがいくつかあります。これは、何百万回もループし、実行に数秒かかる場合があります。ループ内のコードは非常に高速で、最適化されています。何度も行われるので時間がかかります。

例えば:

Screen.Cursor = crHourGlass;
R := FirstRecord;
while R <> nil do begin
  { do something simple with R.Value }
  R := R.NextRecord;
end;
Screen.Cursor := crDefault;

プログラムが応答しなくなることを望まないので、ループ内にApplication.ProcessMessagesを追加します。ただし、追加されたステートメントによってループの速度ができるだけ遅くならないようにする必要もあります。

リンクリストをフォローしているので、利用可能なカウント変数すらありません。間隔が必要な場合は、カウント変数を追加する必要があります。または、タイマーを追加する必要がありますが、時間チェックを最小限に抑える必要があります。

追加されるオーバーヘッドを最小限に抑えるために、これをどのように実装する必要がありますか?


結論:

今のところ、私はAPZ28の答えのようなことをしています。

しかし、長期的には、これを処理するために何らかのスレッドを実装する必要があるようです。Application.ProcessMessagesがそれを行う唯一の方法だと思ったので、これを指摘してくれてありがとう。

4

5 に答える 5

8

ワーク ループをスレッドに配置して、メイン スレッドを GUI ループ処理用に解放できますか。

于 2010-08-04T05:33:35.583 に答える
4

スレッドの下に置くことは簡単ではありません。ロック共有リソースがあればそれが必要だからです。良いトリックはカウンターを持つことです。ループの数を処理した後、ProcessMessages を呼び出します。

var
  LoopCounter: Integer;

LoopCounter := 0;
R := FirstRecord;
while R <> nil do begin
  Inc(LoopCounter);
  if (LoopCounter >= ???) then
  begin
    LoopCounter := 0;
    Application.ProcessMessages;
  end;

  { do something simple with R.Value }
  R := R.NextRecord;
end;
于 2010-08-04T14:58:18.907 に答える
2

最良のオプションは、ループを独自のワーカースレッドに移動して、メインスレッドがブロックされないようにすることです。そうすれば、ProcessMessages()を呼び出す必要はまったくありません。

ただし、メインスレッドでループを実行する必要がある場合は、MsgWaitForMultipleObject()を使用して、ProcessMessages()を呼び出すタイミングを検出できます。

Screen.Cursor = crHourGlass; 
R := FirstRecord; 
while R <> nil do begin 
  { do something simple with R.Value } 
  if MsgWaitForMultipleObjects(0, nil, False, 0, QS_ALLINPUT) = WAIT_OBJECT_0 then
    Application.ProcessMessages;
  R := R.NextRecord; 
end; 
Screen.Cursor := crDefault; 

または、PeekMessage()を使用します。

var Msg: TMsg;

Screen.Cursor = crHourGlass; 
R := FirstRecord; 
while R <> nil do begin 
  { do something simple with R.Value } 
  if PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE) then
    Application.ProcessMessages;
  R := R.NextRecord; 
end; 
Screen.Cursor := crDefault; 

または、GetQueueStatus()を使用します。

Screen.Cursor = crHourGlass; 
R := FirstRecord; 
while R <> nil do begin 
  { do something simple with R.Value } 
  if GetQueueStatus(QS_ALLINPUT) <> 0 then
    Application.ProcessMessages;
  R := R.NextRecord; 
end; 
Screen.Cursor := crDefault; 
于 2010-08-04T19:41:26.250 に答える
2

また、スレッドまたは Anreas のAsyncCallsのようなものに投票します。必要な時間中にユーザーが許可されていないアクションを実行するのを禁止するには、ルーチンの開始時にフラグを設定し、終了時にフラグをリセットできます (とにかく Screen.Cursor を更新する必要があります)。メイン スレッドはこのフラグをチェックし、OnUpdate イベントで影響を受けるすべてのアクションを無効にできます。

于 2010-08-04T06:39:59.250 に答える
1

決定すべき問題の 1 つは、ループが計算しているものに対する答えを得る前に、アプリケーションを続行できるかどうかです。それができない場合、アプリケーションが「レスポンシブ」であることにはあまり意味がありません。プログレス バーなどを更新しようとしている場合は、一定回数の反復ごとにプログレス バーを含むコントロールで .Repaint を呼び出して、プログレス バーを再描画させることができます。

アプリケーションが少なくともしばらくの間続行できる場合は、コードをスレッドに入れることをお勧めします。

ループ コードをスレッドに配置することは、特に処理を中止する可能性がある場合は特に、おそらく合理的です。これまでにスレッドを使用したことがない場合は、学習曲線が少しありますが、説明したような単純なループについては、Web 上に多くの例があります。

于 2010-08-04T06:08:10.403 に答える