我有一段旧代码,我想将其升级到Delphi XE。
我有关于Resume
的编译器警告,我想用Start
替换它,但程序崩溃了。
constructor THTTPGetThread.Create(aAcceptTypes, aAgent, aURL, aFileName, aUserName, aPassword, aPostQuery, aReferer: String; aBinaryData, aUseCache: Boolean; aProgress: TOnProgressEvent; aToFile: Boolean);
begin
FreeOnTerminate := True;
inherited Create(True);
FTAcceptTypes := aAcceptTypes;
FTAgent := aAgent;
FTURL := aURL;
FTFileName := aFileName;
FTUserName := aUserName;
FTPassword := aPassword;
FTPostQuery := aPostQuery;
FTReferer := aReferer;
FTProgress := aProgress;
FTBinaryData := aBinaryData;
FTUseCache := aUseCache;
FTToFile := aToFile;
Resume; <------------ works, but I get compiler warning
//Start; <------------ doesn't work
end;
使用START时出现的错误是:“线程错误:句柄无效(6)” 我不想要复杂的东西(冻结/同步线程)。我只是想从互联网上下载一个文件,而不会阻止GUI。
答案 0 :(得分:15)
简单的答案是,您不应该创建此线程,因为您希望它立即启动。删除对Start
的调用,并将False
传递给继承的构造函数。
请注意,在所有构造函数都运行完成之前,线程才会启动,因此其含义与您发布的代码相同。
至于您的代码失败的原因,请查看以下摘录:
procedure TThread.AfterConstruction;
begin
if not FCreateSuspended and not FExternalThread then
InternalStart(True);
end;
procedure TThread.InternalStart(Force: Boolean);
begin
if (FCreateSuspended or Force) and not FFinished and not FExternalThread then
begin
FSuspended := False;
FCreateSuspended := False;
if ResumeThread(FHandle) <> 1 then
raise EThread.Create(SThreadStartError);
end
else
raise EThread.Create(SThreadStartError);
end;
procedure TThread.Start;
begin
InternalStart(False);
end;
您的代码使用CreateSuspended=True
调用继承的构造函数。这会将FCreateSuspended
设置为True
。然后在Start
运行之前调用TThread.AfterConstruction
。这成功启动了线程,但是,重要的是它将FCreateSuspended
重置为False
。然后当TThread.AfterConstruction
它尝试恢复失败的线程,因为它已经在运行。
我认为Delphi代码很好,因为从构造函数调用Start
是不正确的。在调用Start
之后,您需要确保所有构造函数都运行并派生类构造函数。您还没有任何派生类,但这不是重点 - 重点是不支持从构造函数调用Start
。
最重要的是,您应该创建此线程未暂停,并代表您Start
致电AfterConstruction
。
答案 1 :(得分:7)
问题来自用于确保线程在所有构造函数完成之前不会启动的机制(如David所述)。
你看,无论你作为构造函数的参数传递什么,所有线程实际上都是以挂起状态创建的。这样做是为了确保线程在构造函数完成其工作之前不会实际启动。一旦在AfterConstruction函数中完成所有构造函数(如果CreateSuspended为false),则启动线程。以下是您的问题的相关代码部分:
procedure TThread.AfterConstruction;
begin
if not FCreateSuspended and not FExternalThread then
InternalStart(True);
end;
procedure TThread.Start;
begin
InternalStart(False);
end;
procedure TThread.InternalStart(Force: Boolean);
begin
if (FCreateSuspended or Force) and not FFinished and not FExternalThread then
begin
FSuspended := False;
FCreateSuspended := False;
if ResumeThread(FHandle) <> 1 then
raise EThread.Create(SThreadStartError);
end
else
raise EThread.Create(SThreadStartError);
end;
在您的情况下,当您致电Start时,它可以正常工作。它调用InternalStart,清除FSuspended和FCreateSuspended标志,然后恢复线程。之后,一旦构造函数完成,就执行AfterConstruction。 AfterConstruction检查FCreateSuspended(已在调用Start时清除)并尝试启动线程,但线程已在运行。
为什么要打电话给简历? Resume不会清除FCreateSuspended标志。
所以,总而言之,如果你想让你的线程在创建后自动启动,只需将False传递给TThread构造函数的CreateSuspended参数,它就会像魅力一样工作。
至于Resume / Suspend被弃用的原因...我最好的猜测是,首先暂停线程(在创建时除外)是不好的做法,因为当线程暂停时不保证线程的状态。除其他外,它可以锁定资源。如果所述线程有一个消息队列,它将停止回答它们,这会导致依赖于广播消息的进程出现问题,例如使用ShellExecute打开URL(至少在Internet Explorer中,不确定其他浏览器是否受到影响)。所以,他们弃用了Resume / Suspend,并添加了一个新函数来“恢复”一个被挂起的线程(即Start)。