ロングループでメッセージを処理するとき、私はオーバーヘッド最小限に抑えることができますどのように
-
25-09-2019 - |
質問
私は5月のループ数百万回とは、実行に数秒かかりというのが私の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を追加したいので、私のプログラムは、非応答になりたくありません。しかし、私はまた、追加の文はできるだけ私のループを遅くしたい。
私もカウント変数を用意していないと私は間隔を望んでいた場合は、1つを追加する必要がありますので、私は、リンクリストを以下しています。それとも、私は時間のチェックを最小限にするためにタイマーを追加しますが、必要なければならないと思います。
私が追加されますオーバーヘッドを最小化するためにこれを実装する必要がありますどのように?
<時間>結論ます:
今のところ、私はAPZ28の答えのようなものをやってます。
しかし、それは私がこれを処理するスレッドのいくつかの並べ替えを実装する必要があり、長期のように見えます。私はApplication.ProcessMessagesはそれを行うための唯一の方法だと思ったので、このアウトを指してくれてありがとう。
解決
あなたはGUIのループ処理のためにメインスレッドを解放、スレッドでのワークループを置くことができます。
他のヒント
スレッドの下に入れてはtrivalではありません。良好なトリックループの#を処理した後、カウンタを有している、コール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;
私はまたAnreasのようなスレッドか何か AsyncCalls の投票でしょう。必要な時間の間のいずれかの非許可されたアクションを実行するユーザーを禁止するには、フラグを設定すると、ルーチンが開始され、それは(あなたがとにかくScreen.Cursorを更新する必要がある)の終了時にそれをリセットすることができます。メインスレッドが自分にonUpdateイベントに影響を受けるすべてのアクションを、このフラグをチェックして無効にすることができます。
最良のオプションは、メインスレッドがブロックされていないので、独自のワーカースレッドにループを移動することです、そして、あなたはすべての)ProcessMessagesを(呼び出す必要はありません。
あなたは、メインスレッドでループを行う必要がある場合(ProcessMessagesを呼び出すようにするとき、しかし、その後、あなたが)を検出するMsgWaitForMultipleObjectを()を使用することができ、すなわちます:
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;
を決定する一つの問題は、あなたがループが計算されているものへの答えを持って前に、アプリケーションを続行できるかどうかです。それができない場合は、「応答」というアプリケーションで多くのポイントがありません。あなたはプログレスバーか何かを更新しようとしている場合は、プログレスバーが再描画させるためにプログレスバーを含む制御上の反復ごとに一定数を.Repaintを呼び出すことができます。
アプリケーションを続けることができた場合は、少なくともしばらくの間、スレッド内のコードを置くことは良いアイデアです。
スレッド内ループコードを置くあなたはおそらく処理打ち切りのようなものをしたい場合は特に、とにかくおそらく妥当です。あなたが前にスレッドを使用したことがない場合は、そこに学習曲線のビットがありますが、単純なループのためにあなたがウェブ上の例がたくさんある説明のようにます。