gpt4 book ai didi

ios - ARC (iOS) 下的 Delphi TThread 未发布

转载 作者:技术小花猫 更新时间:2023-10-29 10:27:45 25 4
gpt4 key购买 nike

在 ARC 管理下使用 Delphi for iOS 终止线程的正确方法是什么?

举个简单的例子:

  TMyThread = class(TThread)
protected
procedure Execute; override;
public
destructor Destroy; override;
end;

TForm2 = class(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
FThread: TMyThread;
public
end;

{ TMyThread }
destructor TMyThread.Destroy;
begin

ShowMessage('Destroy');

inherited Destroy;

end;

procedure TMyThread.Execute;
begin

Sleep(5000);

end;

{ TForm2 }
procedure TForm2.Button1Click(Sender: TObject);
begin
FThread := TMyThread.Create(TRUE);
FThread.FreeOnTerminate := TRUE;
FThread.Start;
end;

procedure TForm2.Button2Click(Sender: TObject);
begin
ShowMessage(FThread.RefCount.ToString);
end;

procedure TForm2.Button3Click(Sender: TObject);
begin
FThread := nil;
end;

好的,按下 Button1 将产生一个线程。线程启动后,如果您单击 Button2,它将显示 RefCount 值为 3!好吧,1 是对我的 FThread 变量的引用,TThread 在内部创建了 2 个额外的引用......我深入研究了源代码,发现 RefCount 在这里增加了:

constructor TThread.Create(CreateSuspended: Boolean);

ErrCode := BeginThread(nil, @ThreadProc, Pointer(Self), FThreadID);
if ErrCode <> 0 then
raise EThread.CreateResFmt(@SThreadCreateError, [SysErrorMessage(ErrCode)]);
{$ENDIF POSIX}

在这里:

function ThreadProc(Thread: TThread): Integer;
var
FreeThread: Boolean;
begin
TThread.FCurrentThread := Thread;

嗯...线程完成后(在我的例子中,5 秒后),RefCount 将减少到 2(因为我已将 FreeOnTerminate 设置为 TRUE,但如果我不将 FreeOnTerminate 设置为 TRUE,RefCount 将仍然是 3).

看到问题了吗?如果我调用 FThread := nil,线程永远不会完成并且永远不会调用析构函数,然后,RefCount 应该从 2 减少到 1(或者在 FreeOnTerminate = FALSE 的情况下从 3 减少到 2),并且线程永远不会在 ARC 下释放...

也许我遗漏了一些东西,因为我习惯于使用没有 ARC 的线程...那么,我在这里遗漏了什么?还是ARC下的TThread实现有bug?

也许是TThread的这个定义

private class threadvar
FCurrentThread: TThread;

应该是这样的

private class threadvar
[Weak] FCurrentThread: TThread;

最佳答案

在 qc 中进行一些挖掘后,出现了以下问题和解决方法:

线程参数应该作为const传递

function ThreadProc(Thread: TThread): Integer;  <<-- pass_by_reference pushes
var up the ref_count.
FreeThread: Boolean;
begin
TThread.FCurrentThread := Thread;

如果您将其作为 const 传递,则 ref_count 不会上升到 3。通常这不是问题,因为 ref_count 在函数退出时会减少,但这里:

the function epilog is never exectued because pthread_exit() jumps out of the code.

这只是解决方案的一部分,还需要做更多......

Full workaround by Dave Nottage

After much fiddling around, I've come up with this potential workaround:

为类单元制作这些模组:变化:

function ThreadProc(Thread: TThread): Integer;

到:

function ThreadProc(const Thread: TThread): Integer;

并添加:

TThread.FCurrentThread := nil;

在这一行之后:

if FreeThread then Thread.Free;

覆盖 TThread 后代中的 DoTerminate,因此:

procedure TMyThread.DoTerminate;
begin
try
inherited;
finally
__ObjRelease;
end;
end;

这样调用线程:

FMyThread.Free; // This will do nothing the first time around, since the reference will be nil
FMyThread := TMyThread.Create(True);
// DO NOT SET FreeOnTerminate
FMyThread.OnTerminate := ThreadTerminate;
FMyThread.Resume;

This (at least for me, on the device) results in the thread being destroyed on subsequent calls.

NOTE: Under ARC conditions, never declare a reference to a thread locally, because when it falls out of scope, the thread is destroyed and the Execute method is never called, not to mention other problems it causes.

关于ios - ARC (iOS) 下的 Delphi TThread 未发布,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17031437/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com