2012-05-01 35 views
7

Tôi có một ứng dụng Delphi mà sinh ra 6 chủ đề ẩn danh khi một số sự kiện TTimer.OnTimer.Làm thế nào để chấm dứt chủ đề vô danh trong Delphi trên ứng dụng đóng?

Nếu tôi đóng ứng dụng từ nút X trong thanh tiêu đề Truy cập vi phạm tại địa chỉ $ C0000005 được nêu ra và báo cáo FastMM bị rò rỉ đối tượng TAnonymousThread.

Cách tốt nhất để giải phóng các chuỗi ẩn danh trong Delphi được tạo trong sự kiện OnTimer bằng phương thức TThread.CreateAnonymousThread() là gì?

SOLUTION mà làm việc cho tôi:

tạo một wrapper của bài nặc danh mà chấm dứt họ khi là miễn phí-ed.

type 
    TAnonumousThreadPool = class sealed(TObject) 
    strict private 
    FThreadList: TThreadList; 
    procedure TerminateRunningThreads; 
    procedure AnonumousThreadTerminate(Sender: TObject); 
    public 
    destructor Destroy; override; final; 
    procedure Start(const Procs: array of TProc); 
    end; 

{ TAnonumousThreadPool } 

procedure TAnonumousThreadPool.Start(const Procs: array of TProc); 
var 
    T: TThread; 
    n: Integer; 
begin 
    TerminateRunningThreads; 

    FThreadList := TThreadList.Create; 
    FThreadList.Duplicates := TDuplicates.dupError; 

    for n := Low(Procs) to High(Procs) do 
    begin 
    T := TThread.CreateAnonymousThread(Procs[n]); 
    TThread.NameThreadForDebugging(AnsiString('Test thread N:' + IntToStr(n) + ' TID:'), T.ThreadID); 
    T.OnTerminate := AnonumousThreadTerminate; 
    T.FreeOnTerminate := true; 
    FThreadList.LockList; 
    try 
     FThreadList.Add(T); 
    finally 
     FThreadList.UnlockList; 
    end; 
    T.Start; 
    end; 
end; 

procedure TAnonumousThreadPool.AnonumousThreadTerminate(Sender: TObject); 
begin 
    FThreadList.LockList; 
    try 
    FThreadList.Remove((Sender as TThread)); 
    finally 
    FThreadList.UnlockList; 
    end; 
end; 

procedure TAnonumousThreadPool.TerminateRunningThreads; 
var 
    L: TList; 
    T: TThread; 
begin 
    if not Assigned(FThreadList) then 
    Exit; 
    L := FThreadList.LockList; 
    try 
    while L.Count > 0 do 
    begin 
     T := TThread(L[0]); 
     T.OnTerminate := nil; 
     L.Remove(L[0]); 
     T.FreeOnTerminate := False; 
     T.Terminate; 
     T.Free; 
    end; 
    finally 
    FThreadList.UnlockList; 
    end; 
    FThreadList.Free; 
end; 

destructor TAnonumousThreadPool.Destroy; 
begin 
    TerminateRunningThreads; 
    inherited; 
end; 

End ở đây là làm thế nào bạn có thể gọi nó là:

procedure TForm1.Button1Click(Sender: TObject); 
begin 
    FAnonymousThreadPool.Start([ // array of procedures to execute 
    procedure{anonymous1}() 
    var 
     Http: THttpClient; 
    begin 
     Http := THttpClient.Create; 
     try 
     Http.CancelledCallback := function: Boolean 
      begin 
      Result := TThread.CurrentThread.CheckTerminated; 
      end; 
     Http.GetFile('http://mtgstudio.com/Screenshots/shot1.png', 'c:\1.jpg'); 
     finally 
     Http.Free; 
     end; 
    end, 

    procedure{anonymous2}() 
    var 
     Http: THttpClient; 
    begin 
     Http := THttpClient.Create; 
     try 
     Http.CancelledCallback := function: Boolean 
      begin 
      Result := TThread.CurrentThread.CheckTerminated; 
      end; 
     Http.GetFile('http://mtgstudio.com/Screenshots/shot2.png', 'c:\2.jpg'); 
     finally 
     Http.Free; 
     end; 
    end 
    ]); 
end; 

Không rò rỉ bộ nhớ, tắt máy phù hợp và dễ sử dụng.

+1

'chủ đề ẩn danh' - Tuyệt vời .. Embarcadero đã bỏ mặc gì cho chúng tôi bây giờ? –

+1

@Martin: Không có gì đáng sợ, thực sự. Đó là một luồng có hành vi được cung cấp tại thời điểm tạo bởi một phương thức ẩn danh. Nó cho phép bạn sử dụng các bao đóng khi định nghĩa các luồng. –

+0

Nó liên tục tạo ra/phá hủy các chủ đề, một cái gì đó tôi đã dành 20 năm qua nói với các nhà phát triển để tránh. Tuy nhiên, nếu không thực sự bắt đầu, tôi không thể thấy tại sao nên có một AV. –

Trả lời

14

Nếu bạn muốn duy trì và kiểm soát thời gian tồn tại của luồng thì phải có FreeOnTerminate được đặt thành False. Nếu không, nó là một lỗi để tham khảo chủ đề sau khi nó đã bắt đầu thực hiện. Đó là bởi vì một khi nó bắt đầu thực hiện, bạn đã không có cách nào sẵn sàng để biết liệu nó có được giải phóng hay không.

Cuộc gọi tới CreateAnonymousThread tạo chuỗi có FreeOnTerminate đặt thành True.

The thread is also marked as FreeOnTerminate, so you should not touch the returned instance after calling Start.

Và vì vậy, mặc định, bạn không có quyền kiểm soát tuổi thọ của luồng. Tuy nhiên, bạn có thể đặt FreeOnTerminate thành False ngay trước khi gọi Start. Như thế này:

MyThread := TThread.CreateAnonymousThread(MyProc); 
MyThread.FreeOnTerminate := False; 
MyThread.Start; 

Tuy nhiên, tôi không chắc mình sẽ làm điều đó. Thiết kế của CreateAnonymousThread là chuỗi được tự động giải phóng khi chấm dứt. Tôi nghĩ rằng cá nhân tôi hoặc là sẽ làm theo thiết kế dự định, hoặc lấy nguồn gốc của riêng tôi TThread.

+0

Tôi hiểu. Nhưng tôi vẫn không hiểu. TAnonymousThread là hậu duệ của TThread. Vì vậy, tôi có thể duy trì một danh sách của họ và cố gắng chấm dứt chúng (vì RTL không tự động làm điều đó). Khi tôi cố gắng chấm dứt nó nói: Dự án mtgstudio.exe nêu ra ngoại lệ lớp EThread với thông báo 'Thread Error: Xử lý không hợp lệ (6)'. –

+0

Đọc tài liệu cho chuỗi ẩn danh. Bạn không được phép giữ một tham chiếu đến TThread vì nó sử dụng FreeOnTerminate. –

+0

Xin lỗi, tôi không thể xem dự án này. Tôi nghĩ tôi đã trả lời câu hỏi bạn hỏi. Về cơ bản, bạn bắt đầu một chủ đề ẩn danh bạn mất quyền kiểm soát nó. Bạn không còn có thể tham khảo nó nữa. –

3

Tạo chủ đề của bạn để xem một số loại thông báo từ bên ngoài. Đây có thể là một sự kiện được báo hiệu, một tin nhắn được gửi đến một cửa sổ thuộc sở hữu của luồng, một lệnh được gửi qua một socket mà thread của bạn nghe, hoặc bất kỳ hình thức giao tiếp nào khác mà bạn tìm thấy.

Nếu bạn xác định rằng vấn đề này là do chủ đề của bạn được gọi là chủ đề "ẩn danh", thì giải pháp đơn giản là để bạn biến chúng thành chuỗi không ẩn danh. Đặt phần thân của hàm ẩn danh vào phương thức Execute và chuyển bất kỳ biến nào đã nắm bắt đến lớp chuỗi thông qua hàm tạo của nó.

+0

Bạn có thể dễ dàng truyền một phương thức nặc danh cho một hậu duệ TThread và thực thi nó trong phương thức Execute. –

+0

Tất nhiên, @David. Chẳng phải là bằng chứng 'CreateAnonymousThread' về điều đó sao? Vậy cái gì? –

+1

Tôi có nghĩa là nó sẽ là một thay thế cho cách tiếp cận được đề xuất trong câu cuối cùng của câu trả lời của bạn. –

8

Để tránh lỗi sử dụng CreateAnonymousThread, chỉ cần đặt FreeOnTerminate thành False trước khi bắt đầu.

Bằng cách này bạn có thể làm việc với chuỗi giống như cách bạn thường làm mà không gặp bất kỳ giải pháp nào.

Bạn có thể đọc tài liệu nói rằng CreateAnonymousThread tự động đặt FreeOnTerminate thành True và đây là nguyên nhân gây ra lỗi khi bạn tham chiếu chuỗi.

Các vấn đề liên quan