在单独的TThread中执行操作阻塞GUI线程

7
我使用了这个教程http://delphi.about.com/od/kbthread/a/thread-gui.htm创建了一个类,它在另一个线程中使用TDownLoadURL异步下载互联网上的文件。我这样做是因为我想要下载一个文件而不会阻塞UI线程,这样程序在下载大文件时不会变得无响应,进度条可以更新等等。
但是我遇到了一个问题,即使我在另一个线程中进行了下载(继承自TThread并在Execute方法中完成工作),GUI线程似乎也被阻塞了,并且在下载完成之前不处理消息。这是我的类的代码:http://codepad.org/nArfOPJK(只有99行,是一个简单的类)。我通过在按钮点击事件处理程序中执行以下操作来执行它:
var
    frame: TTProgressFrame;
    dlt: TDownloadThread;
begin
    dlt := TDownloadThread.Create(True);
    dlt.SetFile('C:\ohayo.zip');
    dlt.SetURL('http://download.thinkbroadband.com/512MB.zip');
    dlt.SetFrame(frame);
    dlt.SetApp(Application);
    dlt.Start;

注意:SetApp方法是为了在我的类TDownloadThread的UpdateDownloadProgress方法中手动调用app.ProcessMessages而设计的。这将防止GUI无响应,但会使进度条表现奇怪(aero进度条的移动发光物件移动得太快),因此我将其删除。我想要正确地解决这个问题,如果必须调用ProcessMessages,那么多线程也没有意义。有人能帮我解决这个问题吗?谢谢。

代码看起来正确(包括您的线程代码)。您是否尝试在调试器下运行它并查看dlt.Start执行的速度(即它是否立即返回或者...)? - Eugene Mayevski 'Callback
@Eugene:不,由于某种原因,“Start”没有立即返回,或者根本没有返回,直到下载完成。这是怎么回事? - Kokonotsu
@Kokonotsu,我不知道,但是如果您有源代码,可以将Classes.pas添加到您的项目中,跟踪Start方法并查看其结果。 - Eugene Mayevski 'Callback
@Eugene,不好意思,我没有Classes的源代码。我该怎么办? - Kokonotsu
Delphi 应该包含 VCL 源代码。 - Harriv
如果你真的有 Delphi 2010,那么你就有源代码了。但你不需要将其添加到你的项目中,只需选择“使用调试 DCUs”链接器选项即可。 - Rob Kennedy
1个回答

5
我现在为您提供解决方案!
调用TDownLoadURL.Execute(在TDownloadThread中调用dl.Execute)会导致操作被传回主线程,这就是为什么您的UI变得无响应的原因。
相反,您应该调用ExecuteTarget(nil),它不执行任何这样的操作,并按您的意图工作:下载在工作线程上运行。

@David 看代码,主线程如何等待下载线程完成?就像我说的,Start 不会返回直到下载完成。这是怎么回事? - Kokonotsu
@Kokonotsu 是的,我也在调试代码。TDownloadURL 的设计是通过在主线程之外运行 Execute 方法来工作的。它通过调用 SendAppMessage 返回到那里。 - David Heffernan
@Kokonotsu 我想我现在有答案了!当调用 UpdateProgress 时,请确保将您的调用放回到 Synchronize - David Heffernan
@David,你能给我一个你代码的例子吗?Synchronize一直都在那里。 - Kokonotsu
@Kokonotsu 你需要做的唯一更改是从你提出问题时上传的代码中更改DownloadThread.pas的第97行,将 dl.Execute; 更改为 dl.ExecuteTarget(nil); - David Heffernan
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接