如何 destroy/free 匿名线程
How to destroy/free an anonymousThread
在 windows 上,要停止并销毁匿名线程,我只需执行 FmyTask.free => 这将调用 destroy => 并且在 destroy 内部将设置 terminate = true 并调用 waitfor 等待任务完成 => 最后清理使用的内存
但是在 ARC 上,一切都不同了:(
我使用此代码:
TMyObject
private
FMyTask: TThread;
public
destructor Destroy; override;
procedure DoSomething;
end;
destructor TMyObject.Destroy;
begin
FMyTask.free; // << will do nothing because FMyTask.refcount = 2 !! how it's possible ?
FMyTask:= nil;
inherited;
end;
procedure TMyObject.DoSomething;
begin
FMyTask:= Thread.CreateAnonymousThread(
procedure
begin
sleep(10000000);
end);
FMyTask.FreeOnTerminate := False;
FMyTask.Start;
end;
我除了
什么都不做
MyObject := TmyObject.create;
MyObject.DoSomething;
MyObject.free;
MyObject := nil;
但是如您所见,在 TmyObject 的 onDestroy 中,FMyTask 的引用计数为 2!所以这意味着 FMyTask 不会被 TmyObject 破坏(refcount 将减少到 1)(但稍后)并且你想象所有可能导致的错误 :(
sean 对 fMyTask 的引用保留在此函数中:
function ThreadProc(const Thread: TThread): Integer;
var
FreeThread: Boolean;
{$IFDEF MACOS}
pool: Pointer;
{$ENDIF MACOS}
begin
{$IFDEF AUTOREFCOUNT}
Thread.__ObjAddRef; // this ensures the instance remains for as long as the thread is running
{$ENDIF}
TThread.FCurrentThread := Thread;
{$IF Defined(POSIX)}
if Thread.FSuspended then
pthread_mutex_lock(Thread.FCreateSuspendedMutex);
{$ENDIF POSIX}
{$IFDEF MACOS}
// Register the auto release pool
pool := objc_msgSend(objc_msgSend(objc_getClass('NSAutoreleasePool'),
sel_getUid('alloc')), sel_getUid('init'));
{$ENDIF MACOS}
try
Thread.FStarted := True;
if not Thread.Terminated then
try
Thread.Execute;
except
Thread.FFatalException := AcquireExceptionObject;
end;
finally
Result := Thread.FReturnValue;
FreeThread := Thread.FFreeOnTerminate;
Thread.DoTerminate;
Thread.FFinished := True;
SignalSyncEvent;
if FreeThread then
begin
Thread.DisposeOf;
{$IFDEF AUTOREFCOUNT}
Thread.__ObjRelease; // This will clear the thread reference that was added by setting FreeOnTerminate.
{$ENDIF}
end;
{$IFDEF AUTOREFCOUNT}
Thread.__ObjRelease; // This will clear the thread reference we added above. This may initiate disposal.
{$ENDIF}
{$IFDEF USE_LIBICU}
// Destroy Collator Cache
ClearCollatorCache;
{$ENDIF}
{$IF Defined(MSWINDOWS)}
EndThread(Result);
{$ELSEIF Defined(POSIX)}
{$IFDEF MACOS}
// Last thing to do in thread is to drain the pool
objc_msgSend(pool, sel_getUid('drain'));
{$ENDIF MACOS}
{$IFDEF ANDROID}
// Detach the NativeActivity virtual machine to ensure the proper relase of JNI context attached to the current thread
PJavaVM(System.JavaMachine)^.DetachCurrentThread(PJavaVM(System.JavaMachine));
{$ENDIF ANDROID}
// Directly call pthread_exit since EndThread will detach the thread causing
// the pthread_join in TThread.WaitFor to fail. Also, make sure the EndThreadProc
// is called just like EndThread would do. EndThreadProc should not return
// and call pthread_exit itself.
if Assigned(EndThreadProc) then
EndThreadProc(Result);
pthread_exit(Result);
{$ENDIF POSIX}
end;
end;
这是正常行为吗?如果是的话,我是唯一一个认为 arc 更糟糕的人 delphi ?
ARC 必须保留对线程的额外引用,以免创建者超出范围时过早释放线程。
在非 ARC 下,当您调用 FMyThread.Free
时,会调用 Destroy
析构函数,随后会调用 Terminate;
和 WaitFor;
使用 ARC,调用 Free
会减少引用计数并检查是否为零。由于还剩下一个引用,所以没有调用析构函数。
这意味着释放线程(使用 FreeOnTerminate=false
)的模式应该是:
fMyThread.Terminate;
fMyThread.WaitFor; // Wait for the thread to finish
fMyThread.Free;
这意味着 ARC 占用的额外引用被移除,线程对象将在创建者超出范围后随时释放。
请注意,这种释放线程的模式(使用 FreeOnTerminate=false
)适用于所有编译器,并且在非 ARC 区域终止线程时也避免了罕见的竞争条件。
设置为 FreeOnTerminate = true
的所有线程的一般规则是它们不应通过引用从外部访问。
在 windows 上,要停止并销毁匿名线程,我只需执行 FmyTask.free => 这将调用 destroy => 并且在 destroy 内部将设置 terminate = true 并调用 waitfor 等待任务完成 => 最后清理使用的内存
但是在 ARC 上,一切都不同了:( 我使用此代码:
TMyObject
private
FMyTask: TThread;
public
destructor Destroy; override;
procedure DoSomething;
end;
destructor TMyObject.Destroy;
begin
FMyTask.free; // << will do nothing because FMyTask.refcount = 2 !! how it's possible ?
FMyTask:= nil;
inherited;
end;
procedure TMyObject.DoSomething;
begin
FMyTask:= Thread.CreateAnonymousThread(
procedure
begin
sleep(10000000);
end);
FMyTask.FreeOnTerminate := False;
FMyTask.Start;
end;
我除了
什么都不做MyObject := TmyObject.create;
MyObject.DoSomething;
MyObject.free;
MyObject := nil;
但是如您所见,在 TmyObject 的 onDestroy 中,FMyTask 的引用计数为 2!所以这意味着 FMyTask 不会被 TmyObject 破坏(refcount 将减少到 1)(但稍后)并且你想象所有可能导致的错误 :(
sean 对 fMyTask 的引用保留在此函数中:
function ThreadProc(const Thread: TThread): Integer;
var
FreeThread: Boolean;
{$IFDEF MACOS}
pool: Pointer;
{$ENDIF MACOS}
begin
{$IFDEF AUTOREFCOUNT}
Thread.__ObjAddRef; // this ensures the instance remains for as long as the thread is running
{$ENDIF}
TThread.FCurrentThread := Thread;
{$IF Defined(POSIX)}
if Thread.FSuspended then
pthread_mutex_lock(Thread.FCreateSuspendedMutex);
{$ENDIF POSIX}
{$IFDEF MACOS}
// Register the auto release pool
pool := objc_msgSend(objc_msgSend(objc_getClass('NSAutoreleasePool'),
sel_getUid('alloc')), sel_getUid('init'));
{$ENDIF MACOS}
try
Thread.FStarted := True;
if not Thread.Terminated then
try
Thread.Execute;
except
Thread.FFatalException := AcquireExceptionObject;
end;
finally
Result := Thread.FReturnValue;
FreeThread := Thread.FFreeOnTerminate;
Thread.DoTerminate;
Thread.FFinished := True;
SignalSyncEvent;
if FreeThread then
begin
Thread.DisposeOf;
{$IFDEF AUTOREFCOUNT}
Thread.__ObjRelease; // This will clear the thread reference that was added by setting FreeOnTerminate.
{$ENDIF}
end;
{$IFDEF AUTOREFCOUNT}
Thread.__ObjRelease; // This will clear the thread reference we added above. This may initiate disposal.
{$ENDIF}
{$IFDEF USE_LIBICU}
// Destroy Collator Cache
ClearCollatorCache;
{$ENDIF}
{$IF Defined(MSWINDOWS)}
EndThread(Result);
{$ELSEIF Defined(POSIX)}
{$IFDEF MACOS}
// Last thing to do in thread is to drain the pool
objc_msgSend(pool, sel_getUid('drain'));
{$ENDIF MACOS}
{$IFDEF ANDROID}
// Detach the NativeActivity virtual machine to ensure the proper relase of JNI context attached to the current thread
PJavaVM(System.JavaMachine)^.DetachCurrentThread(PJavaVM(System.JavaMachine));
{$ENDIF ANDROID}
// Directly call pthread_exit since EndThread will detach the thread causing
// the pthread_join in TThread.WaitFor to fail. Also, make sure the EndThreadProc
// is called just like EndThread would do. EndThreadProc should not return
// and call pthread_exit itself.
if Assigned(EndThreadProc) then
EndThreadProc(Result);
pthread_exit(Result);
{$ENDIF POSIX}
end;
end;
这是正常行为吗?如果是的话,我是唯一一个认为 arc 更糟糕的人 delphi ?
ARC 必须保留对线程的额外引用,以免创建者超出范围时过早释放线程。
在非 ARC 下,当您调用 FMyThread.Free
时,会调用 Destroy
析构函数,随后会调用 Terminate;
和 WaitFor;
使用 ARC,调用 Free
会减少引用计数并检查是否为零。由于还剩下一个引用,所以没有调用析构函数。
这意味着释放线程(使用 FreeOnTerminate=false
)的模式应该是:
fMyThread.Terminate;
fMyThread.WaitFor; // Wait for the thread to finish
fMyThread.Free;
这意味着 ARC 占用的额外引用被移除,线程对象将在创建者超出范围后随时释放。
请注意,这种释放线程的模式(使用 FreeOnTerminate=false
)适用于所有编译器,并且在非 ARC 区域终止线程时也避免了罕见的竞争条件。
设置为 FreeOnTerminate = true
的所有线程的一般规则是它们不应通过引用从外部访问。