5

インターネットからファイルをダウンロードする Delphi アプリケーションを作成しています。サーバーが範囲要求をサポートしている場合、マルチスレッドになります。進行状況も GUI に中継されます。

現在のソフトウェア モデルはTThreadコンポーネントを使用しています。GUI は を呼び出し、TDownloadThread次に を生成しTDownloadPartThreadsます。これらは、'WinHttp' を介して実際にダウンロードを行うスレッドです。

私の問題:4つのスレッドしかダウンロードしていない1つのダウンロードでも、CPUが使い果たされます。

私の想定される原因:

  1. 宛先ファイルに 8192 バイトごとに書き込みを行っていますが、1 つのブロックに書き込む前にバッファリングする必要があるかどうか疑問に思っていました。
  2. スレッド通信はSynchronize(MainForm.UpdateProgress(Downloaded, TotalSize))、私が聞いた中で行われるのはひどいものです。おそらく、スレッド間でオブジェクトを共有して、メインフォームのタイマーを使用してこれにアクセスし、進行状況を更新できるようにする必要がありますか?

私のソリューション

  1. ファイルの書き込みをずらして、すべてのxバイトのみを書き込みます。

  2. TThread使用するコンポーネントを更新し、OmniThreadLibrary何らかの形でデータをメイン フォームに送り返します。その後、各TDownloadPartスレッドは になりIOmniWorker、オブジェクトをメイン フォームと共有することで進行状況を送り返します。次に、メイン フォームはタイマーを使用して、次のように進行状況を更新します。ProgressBar1.Position := sharedDataObject.Progress;

うまくいけば、誰かが私を正しい方向に向けることができます!

4

4 に答える 4

2

2番目のソリューションで提案したように、共有オブジェクトを使用して状態を更新します。8 バイト (4 では十分ではありません!) のファイル サイズのみを共有し、各共有場所のアドレスが 8 にアラインされていることを確認すると、インターロック命令を使用してこの共有状態を変更でき、ロックする必要がなくなります。 .

共有状態を維持する最も簡単な方法は、GpStuffユニットの TGp8AlignedInt64 レコードです。これは、OmniThreadLibrary または TThread ベースのソリューションでも同様に機能します。

TGp8AlignedInt64 = record
  function  Add(value: int64): int64; inline;
  function  Addr: PInt64; inline;
  function  CAS(oldValue, newValue: int64): boolean;
  function  Decrement: int64; overload; inline;
  function  Decrement(value: int64): int64; overload; inline;
  function  Increment: int64; overload; inline;
  function  Increment(value: int64): int64; overload; inline;
  function  Subtract(value: int64): int64; inline;
  property Value: int64 read GetValue write SetValue;
end; 

このレコードに対するすべての操作はスレッド セーフであるため、ワーカー スレッドで .Add を安全に実行し、同時にメイン フォームのタイマー イベントから .Value を呼び出すことができます。

于 2011-11-14T15:35:00.543 に答える
1

Synchronize() を使用すると、追加のスレッドがメイン スレッドで待機するため、プログラムの速度が低下する可能性がありますが、CPU の負担にはなりません。お使いのコンピューターは以前よりも多くの作業を行うことはありませんが、GUI への応答性に影響がある場合、ユーザーはそれに気付く可能性があります。

ディスクへの書き込みは IO 集中型になる可能性がありますが、CPU の負担にはなりません。場合によっては、ウイルス対策プログラムが書き込みと読み取りの CPU 使用率を増加させ、暗号化されたファイル システムによって CPU 使用率がわずかに増加することがあります。

あなたが言及した2つの問題はどちらも、それ自体がCPU使用率に影響を与えるものではないため、それらを変更しても必ずしも問題が軽減されるとは限りません.

おそらく、GUI を頻繁に更新しすぎているか、GUI を更新するコードが CPU を集中的に使用していますか? GUI を更新するためにコールバックを停止するとどうなりますか? CPU使用率は下がりますか?

おそらく、ディスクに書き込んでいるコードが何らかの方法でそれを処理しているのではないでしょうか? どのようにデータをバッファリングしていますか?

高い CPU 使用率の本当の原因が何であるかを明確に見つけてください。

ボトルネックがインターネットの帯域幅以外にある場合は、問題がある可能性が高くなります。

于 2011-11-14T16:46:47.927 に答える
1

うん-それはすべて、GUIの更新頻度に依存します。TThread.Synchronize は、メイン スレッドの更新で考えられる最悪のオプションです。ダウンロード スレッドは、続行する前に、GUI の更新が実行されるまで待機する必要があります。リストの次は PostMessaging の更新で、これは中間点を満たします。ダウンロード スレッドはもはや待機する必要はありませんが、投稿されたメッセージに GUI が追いつかない場合は、WMQ の 10000 メッセージ制限に達するずっと前にフリーズします。到達しました。多くのスレッドと急速な更新がある場合、適切な通知オブジェクト/リスト/配列/何でも合理的な解決策をポーリングするタイマー。

于 2011-11-14T15:15:46.033 に答える
1

私のアドバイスは、何が遅いのかを「推測」するのではなく、プロファイラーを使用して、CPU が燃えている場所を測定することです。きっと驚かれると思います。

WinHTTP は遅くなく、それ自体で多くの CPU を使用しません。これは WinINet よりもはるかに高速で、非常にうまく機能しています (少なくともフラットな C API では - それとも COM インターフェイスを使用していますか?)。おそらく、コードに問題がある可能性があります。

ご質問について:

  1. 8192 バイトのチャンク サイズを書き込むことは理にかなっていますが、より大きなバッファーを使用する場合、(HTTP ストリームのダウンロード速度と比較すると) それほど速くはなりません。Windows ファイル システムは通常、データを 4 KB ずつディスクに書き込み、バッファリングをすべて自動的に行います。大きくしてみてください (例: 65536) が、変化は目立たないと思います。

  2. Synchronizeそれほどひどいものではありません。できることは、毎回ではなく、いくつかのパーセント (たとえば、5% または 10% ごと) で変更する場合にのみ呼び出すことです。最新の通知さ​​れたサイズを含むプライベート変数を追加するだけで、ダウンロード スレッド内でそれを行うことができます。

もう 1 つの可能性は、いくつかの読み取り専用プロパティ ( DownloadedSize + TotalSize: Int64) をスレッド クラスに使用し、ダウンロード中にその内容を更新することです。次に、メイン GUI スレッドでTTimer- またはカスタム メッセージ ( WM_USER+...) を作成PostMessage()し、ダウンロード スレッドで使用して、必要に応じて各スレッドの進行状況バーを更新します。これは、メイン スレッドから一部のプロパティを安全に読み取ることができます。

于 2011-11-14T16:07:41.240 に答える