如何销毁/释放anonymousThread

时间:2016-10-08 11:26:53

标签: delphi automatic-ref-counting firemonkey

在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(refcount将减少到1)与TmyObject(但后来),你想象所有可能导致的错误:(

它的意思是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;

这是正常行为吗?如果是的话,我是唯一一个认为电弧是对德尔福更糟糕的想法的人吗?

1 个答案:

答案 0 :(得分:4)

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的所有线程的一般规则是不应该通过引用从外部访问它们。