Delphi:是否应该创建一个"未暂停"的线程?

Ian*_*oyd 9 delphi multithreading delphi-5 jvcl

我一直试图追踪Jedi VCL中的内存泄漏JvHidControllerClass.pas,我在源历史中遇到了这种变化:

旧版本:

constructor TJvHidDeviceReadThread.CtlCreate(const Dev: TJvHidDevice);
begin
  inherited Create(True);
  Device := Dev;
  NumBytesRead := 0;
  SetLength(Report, Dev.Caps.InputReportByteLength);
end;
Run Code Online (Sandbox Code Playgroud)

目前的修订:

constructor TJvHidDeviceReadThread.CtlCreate(const Dev: TJvHidDevice);
begin
  inherited Create(False);
  Device := Dev;
  NumBytesRead := 0;
  SetLength(Report, Dev.Caps.InputReportByteLength);
end;
Run Code Online (Sandbox Code Playgroud)

根据经验,我发现如果你创建一个挂起的线程:

inherited Create(False);
Run Code Online (Sandbox Code Playgroud)

然后线程立即开始运行.在这种情况下,它将尝试访问尚未初始化的对象:

procedure TJvHidDeviceReadThread.Execute;
begin
   while not Terminated do
   begin
     FillChar(Report[0], Device.Caps.InputReportByteLength, #0);
     if Device.ReadFileEx(Report[0], Device.Caps.InputReportByteLength, @DummyReadCompletion) then
Run Code Online (Sandbox Code Playgroud)

马上尝试填充Report,并访问该对象Device.问题是它们尚未初始化; 这些是线程启动后的下一行:

  Device := Dev;
  NumBytesRead := 0;
  SetLength(Report, Dev.Caps.InputReportByteLength);
Run Code Online (Sandbox Code Playgroud)

我意识到这是一种竞争条件; 用户在生产中遇到崩溃的可能性非常低,因此离开竞赛失败可能是无害的.

但我离开了吗?我错过了什么吗?打电话:

BeginThread(nil, 0, @ThreadProc, Pointer(Self), Flags, FThreadID);
Run Code Online (Sandbox Code Playgroud)

没有立即启动线程并立即运行?这是否真的是(有意)添加到JVCL的竞争条件回归?是否有一些秘密

CreateSuspended(False);
Run Code Online (Sandbox Code Playgroud)

这使它成为正确的代码:

CreateSuspended(True);
...
FDataThread.Resume;
Run Code Online (Sandbox Code Playgroud)

被错误地打电话烧了之后

TMyThread.Create(False)
Run Code Online (Sandbox Code Playgroud)

我把它归于我的大脑,因为它永远不正确.是否有任何有效的用途让线程立即启动(当你必须初始化值时)?

Dav*_*nan 12

这是Delphi 5实现的基本设计缺陷TThread.底层Windows线程在构造函数中启动TThread.这导致你描述的种族.

在Delphi 6版本的RTL中,线程启动机制已更改.从Delphi 6开始,线程启动TThread.AfterConstruction.并且在构造函数完成后运行.这将使您的代码免费竞争.

在Delphi 6及更高版本中,底层Windows线程是在TThread构造函数中创建的,但是使用该CREATE_SUSPENDED标志创建了挂起.然后AfterConstruction,只要TThread.FCreateSuspendedFalse,线程就会恢复.

解决Delphi 5中的问题的一种方法是最后调用继承的构造函数.像这样:

constructor TJvHidDeviceReadThread.CtlCreate(const Dev: TJvHidDevice);
begin
  Device := Dev;
  NumBytesRead := 0;
  SetLength(Report, Dev.Caps.InputReportByteLength);
  inherited Create(False);
end;
Run Code Online (Sandbox Code Playgroud)

相当难看,我知道.

因此,创建线程暂停并在构造函数完成后恢复的方法可能更好.这种方法反映了RTL如何解决Delphi 6及更高版本中的问题.