2013-06-10 1 views
13

ARC 관리에서 iOS 용 Delphi를 사용하여 스레드를 종료하는 적절한 방법은 무엇입니까? ARC (iOS)에서 Delphi TThread가 출시되지 않음

이 간단한 예를 들어 보자

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은 내부적으로 생성이 추가 참조가 ... 나는 소스 코드에 팠하고 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 구현에 버그가 있습니까? 다음과 같은 문제 및 해결 방법 품질 관리의 뒷조사 후

private class threadvar 
    [Weak] FCurrentThread: TThread; 
+0

'FCurrentThread'가 소멸자에서 해제되지 않았습니까? –

+0

FCurrentThread는 TThread의 클래스 threadvar 변수이며 인스턴스의 변수는 아니지만 TThread의 클래스 변수입니다. 이 변수는 절대로 바뀌지 않습니다. 코드로 판단하면 다른 스레드를 생성하면 바뀌어야하지만 ... 테스트되지는 않습니다. 그래서 ... 내 스레드 소멸자는 호출되지 않습니다 – Eric

+0

다른 스레드를 생성하여 변경되지 않습니다. 각 OS 스레드는 threadvar이기 때문에 해당 변수의 자체 버전을 가져옵니다. GetCurrentThreadID와 동일한 TThread입니다. –

답변

3

같은

어쩌면 TThread

private class threadvar 
    FCurrentThread: TThread; 

의 정의가 있어야합니다 뭔가 표시 :

스레드 매개 변수가 전달되어야한다 예 : const

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

여기 당신이 ref_count 함수의 출구에 감소 도착하기 때문에 ref_count는, 일반적으로 이것은 문제가되지 않습니다 3. 까지 사라하지 않은 것 const로 통과했지만 :

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

이것은 해결책의 일부일뿐입니다. 변경 :

function ThreadProc(Thread: TThread): Integer; 

은 클래스 단위로 이러한 개조를 확인 :

Full workaround by Dave Nottage

많은 주변 손보는 후,이 잠재적 인 해결 방법을 마련했습니다

to :

function ThreadProc(const Thread: TThread): Integer; 

및 추가

TThread.FCurrentThread := nil; 

을이 줄 끝 :

if FreeThread then Thread.Free; 

재정 DoTerminate TThread의 자손에 따라서 :

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; 

이 장치는 적어도 이후의 호출에서 스레드가 파괴 될 수 있습니다.

참고 :이 범위를 벗어날 경우, 스레드가 파괴되고 호출되지 않습니다되고 그것이 원인이 다른 문제를 언급하지 않기 위하여, 방법을 실행하기 때문에 아래에서 ARC 조건, 결코는 로컬 스레드에 대한 참조를 선언합니다.