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

时间:2013-07-19 14:17:15

标签: multithreading delphi 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;

当前版本:

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

根据经验,我发现如果你创建一个而不是暂停的帖子:

inherited Create(False);

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

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

立即尝试填充Report,然后访问对象Device。问题是它们尚未初始化;这些是线程启动后的 next 行:

  Device := Dev;
  NumBytesRead := 0;
  SetLength(Report, Dev.Caps.InputReportByteLength);

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

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

BeginThread(nil, 0, @ThreadProc, Pointer(Self), Flags, FThreadID);

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

有一些秘密
CreateSuspended(False);

使其成为正确的代码:

CreateSuspended(True);
...
FDataThread.Resume;

被错误地打电话烧伤后

TMyThread.Create(False)

我已将其归档于我的大脑中,因为从不正确。是否有任何有效的用途让线程立即启动(当你必须初始化值时)?

1 个答案:

答案 0 :(得分:12)

这是使用TThread的Delphi 5实现的基本设计缺陷。底层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;

我知道相当难看。

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