2013-06-10 37 views
13

Một cách thích hợp để chấm dứt một chuỗi bằng cách sử dụng Delphi cho iOS trong quản lý ARC là gì?Delphi TThread dưới ARC (iOS) không được phát hành

Hãy ví dụ đơn giản này:

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; 

Ok, nhấn Button1 sẽ đẻ trứng một sợi. Sau khi thread được bắt đầu, nếu bạn nhấp vào Button2 nó sẽ hiển thị một giá trị RefCount của 3 !! Vâng, 1 là tài liệu tham khảo để biến FThread của tôi, và có 2 tài liệu tham khảo bổ sung mà TThread tạo ra trong nội bộ ... Tôi đã đào vào mã nguồn và phát hiện ra rằng RefCount được tăng lên ở đây:

constructor TThread.Create(CreateSuspended: Boolean); 

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

Và ở đây:

Vâng ... Sau khi hoàn tất (Trong trường hợp của tôi, sau 5 giây), RefCount sẽ giảm xuống 2 (Vì tôi đã đặt FreeOnTerminate thành TRUE, nhưng nếu tôi không đặt FreeOnTerminate thành TRUE, thì RefCount sẽ vẫn là 3).

Xem sự cố? Chủ đề là không bao giờ hoàn thành và destructor không bao giờ được gọi là, nếu tôi gọi FThread := nil, sau đó, RefCount nên giảm từ 2 đến 1 (Hoặc từ 3 ... Điện thoại

Có lẽ tôi đang thiếu một cái gì đó bởi vì tôi đang sử dụng để làm việc với các chủ đề không có ARC ... Vì vậy, những gì tôi thiếu ở đây? Hoặc có lỗi trong triển khai TThread trong ARC không?

Có lẽ định nghĩa này của TThread

private class threadvar 
    FCurrentThread: TThread; 

nên một cái gì đó giống như

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

Không phải 'FCurrentThread' được phát hành trong trình hủy không? –

+0

FCurrentThread là một biến threadvar lớp của TThread, nó không phải là một biến từ cá thể, mà là một biến lớp từ TThread. Biến này không bao giờ được lưu trữ. Đánh giá bởi mã, nó sẽ thay đổi nếu tôi sinh ra một sợi khác ... nhưng tôi chưa thử nghiệm Vì vậy ... Trình hủy chỉ của tôi không bao giờ được gọi là – Eric

+0

Nó sẽ không thay đổi bằng cách sinh ra một luồng khác. Mỗi chuỗi hệ điều hành có phiên bản riêng của biến đó bởi vì đó là một luồng. Đó là tương đương TThread của GetCurrentThreadID. –

Trả lời

3

Sau khi một số digging trong qc các vấn đề và cách giải quyết sau đây xuất hiện:

thông số chủ đề nên được thông qua như const

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

Đã bạn thông qua nó như const các ref_count sẽ không đi lên đến 3. Thông thường đây không phải là một vấn đề, bởi vì ref_count bị giảm trên lối ra của hàm, nhưng ở đây:

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

Đây chỉ là một phần của giải pháp, cần thực hiện thêm một chút ...

Full workaround by Dave Nottage

Sau nhiều quan trọng xung quanh, tôi đã đưa ra cách giải quyết đầy tiềm năng này:

Thực hiện các mods cho đơn vị Lớp học: Thay đổi:

function ThreadProc(Thread: TThread): Integer; 

tới:

function ThreadProc(const Thread: TThread): Integer; 

và thêm:

TThread.FCurrentThread := nil; 

sau dòng này:

if FreeThread then Thread.Free; 

Override DoTerminate trong hậu duệ TThread, như sau:

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

Gọi thread như sau:

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; 

Điều này (ít nhất là đối với tôi, trên thiết bị) dẫn đến chuỗi bị hủy trong các cuộc gọi tiếp theo. LƯU Ý: Trong điều kiện ARC, không bao giờ khai báo tham chiếu đến một chuỗi cục bộ, bởi vì khi nó nằm ngoài phạm vi, chuỗi bị hủy và phương thức Thực thi không bao giờ được gọi, chưa kể đến các vấn đề khác mà nó gây ra.