我正在创建一个Delphi应用程序来从Internet下载文件,如果服务器支持范围请求,它将是多线程的。进度也被转发回GUI。
当前的软件模型使用TThread
个组件。 GUI调用TDownloadThread
然后生成TDownloadPartThreads
- 这些是实际通过'WinHttp'进行下载的线程。
我的问题:即使只下载4个线程的下载,CPU也会用完。
我假设的原因:
Synchronize(MainForm.UpdateProgress(Downloaded, TotalSize))
完成的,我听说是AWFUL要做的,也许我应该在线程之间共享一个对象,以便我可以使用主窗体上的计时器来访问它,以更新进度?< / LI>
醇>
我的解决方案
错开文件写入,只写每x
个字节。
更新TThread
组件以使用OmniThreadLibrary
并以某种方式将数据发送回主表单。然后,每个TDownloadPart
线程将成为IOmniWorker
,并通过与主表单共享对象来发回其进度。然后,主窗体将使用计时器更新其进度,例如:ProgressBar1.Position := sharedDataObject.Progress;
希望有人能指出我正确的方向!
答案 0 :(得分: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并同时从主窗体中的timer事件调用.Value。
答案 1 :(得分:1)
是的 - 这一切都取决于GUI更新的频率。 TThread.Synchronize是主线程更新最糟糕的选择 - 下载线程被迫等待GUI更新,然后才能继续。列表中的下一个是PostMessaging更新,这填补了中间地带 - 下载线程不再需要等待,但是,如果GUI无法跟上发布的消息,它将在WMQ的10000消息限制之前很久就冻结到达。在有许多线程和快速更新的情况下,定时器轮询一些合适的通知对象/列表/数组/什么是明智的解决方案。
答案 2 :(得分:1)
我的建议不是猜测&#34;什么是慢,但使用分析器和测量 CPU被烧毁。我怀疑你可能会感到惊讶。
WinHTTP并不慢,并且本身不使用大量CPU。它比WinINet快得多,并且运行良好(至少使用扁平的C API - 或者您使用的是COM接口吗?)。也许你的代码有问题。
关于您的问题:
写入8192字节的块大小确实有意义,如果使用更大的缓冲区,则不会更快(与HTTP流下载速度相比)。 Windows文件系统通常将磁盘上的数据写入4 KB,并自行完成缓冲。试着让它变大(例如65536),但我不认为变化会很明显。
Synchronize
并不是那么糟糕。你可以做的只是在你改变一些百分比(例如每5%或10%)而不是每次改变时才调用它。您可以在下载线程中执行此操作,只需添加包含最新通知大小的私有变量。
另一种可能性是对线程类使用一些只读属性(DownloadedSize + TotalSize: Int64
),然后在下载期间更新其内容。然后使用TTimer
- 或创建自定义消息(WM_USER+...
),然后在下载线程中使用PostMessage()
- 在主GUI线程中为每个线程刷新进度条(如果需要) 。从主线程读取某些属性是安全的。
答案 3 :(得分:1)
Synchronize()可能会降低你的程序速度,因为你的主线程上会有你的额外线程等待,但是,这不是CPU的负担。您的计算机将不再进行任何工作,但如果用户的GUI响应速度很快,用户可能会注意到它。
写入磁盘可能是IO密集型的,但同样不会给CPU带来负担。有时您的防病毒程序会增加写入和读取时的CPU使用率,而加密文件系统会引入一点额外的CPU使用率。
由于您提到的两个问题都不会影响CPU使用率,因此更改它们不一定能解决问题。
也许您经常更新GUI,或者更新GUI的代码太过CPU密集?如果您停止回拨更新GUI会发生什么?它是否会降低CPU使用率?
也许写入磁盘的代码是以某种方式处理它的?你是如何缓冲数据的呢?
绝对找出高CPU使用率的真正原因。
如果您发现您的瓶颈不是互联网带宽,那么很可能存在问题。