2012-05-01 2 views
7

У меня есть приложение Delphi, которое порождает 6 анонимных потоков при некотором событии TTimer.OnTimer.Как закрыть анонимные потоки в Delphi на приложении закрыть?

Если я закрою приложение с помощью кнопки X в строке заголовка, то будет нарушено нарушение доступа по адресу $ C0000005, а FastMM сообщит об утечке объектов TAnonymousThread.

Какой способ является бесплатным анонимным потоком в Delphi, созданным в рамках события OnTimer с помощью метода TThread.CreateAnonymousThread()?

РЕШЕНИЕ, который работал для меня:

создал оболочку анонимных нитей, которые оканчиваются их на будучи свободным ред.

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 здесь нет, как можно назвать:

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; 

Нет утечек памяти, правильное выключение и простой в использовании.

+1

'анонимные темы' - О, отлично .. что теперь навязал нам Embarcadero? –

+1

@ Мартин: На ​​самом деле ничего страшного. Это поток, поведение которого предоставляется во время создания анонимным методом. Он позволяет использовать замыкания при определении потоков. –

+0

Он постоянно создает/уничтожает потоки, что я провел последние 20 лет, чтобы разработчики избегали. Тем не менее, если не начать, я не понимаю, почему должен быть AV. –

ответ

14

Если вы хотите поддерживать и контролировать время жизни нити, то он должен иметь FreeOnTerminate, установленный в False. В противном случае это ошибка для ссылки на поток после его запуска. Это потому, что как только он начинает выполняться, у вас нет готового способа узнать, освобожден ли он.

Звонок CreateAnonymousThread создает нить с FreeOnTerminate, установленную на True.

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

И так, но по умолчанию вы не можете контролировать жизненный цикл нити. Однако вы можете установить FreeOnTerminate на номер False непосредственно перед вызовом Start. Например:

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

Однако я не уверен, что сделаю это. Конструкция CreateAnonymousThread состоит в том, что поток автоматически освобождается после завершения. Я думаю, что лично я буду либо следовать намеченному дизайну, либо получить собственный TThread потомок.

+0

Я вижу. Но я все еще не понимаю. TAnonymousThread является потомком TThread. Поэтому я могу сохранить их список и попытаться их прервать (так как RTL не делает это автоматически). Когда я пытаюсь закончить, он говорит: Project mtgstudio.exe поднял класс исключения EThread с сообщением «Ошибка потока: дескриптор недействителен (6)». –

+0

Прочитайте документы для анонимной нити. Вы не можете хранить ссылку на TThread, так как она использует FreeOnTerminate. –

+0

Извините, я не могу посмотреть этот проект. Думаю, я ответил на заданный вами вопрос. По сути, вы запускаете анонимный поток, который вы теряете над ним контроль. Вы больше не можете ссылаться на него. –

3

Заставьте свои темы следить за каким-либо уведомлением извне. Это может быть событие, которое получает сигнал, сообщение, отправленное в окно, принадлежащее потоку, команду, отправленную через сокет, который прослушивает ваш поток, или любую другую форму связи, которую вы найдете.

Если вы определили, что эта проблема связана с тем, что ваши потоки являются так называемыми «анонимными» потоками, тогда для вас это упростит, чтобы сделать их не анонимными. Поместите тело анонимной функции в метод Execute и передайте любые захваченные переменные классу потоков через его конструктор.

+0

Вы можете легко передать анонимный метод потомку TThread и выполнить его в методе Execute. –

+0

Конечно, @ Давид. Это не доказательство «CreateAnonymousThread»? И что? –

+1

Я имею в виду, что это будет альтернативой подходу, предложенному в заключительном предложении вашего ответа. –

8

Чтобы избежать ошибок, используя CreateAnonymousThread, просто установите FreeOnTerminate на номер False перед его запуском.

Таким образом, вы можете работать с нитью, как обычно, без каких-либо обходных решений.

Вы можете прочитать документацию, в которой говорится, что CreateAnonymousThread автоматически устанавливает FreeOnTerminate в True, и это то, что вызывает ошибки при обращении к потоку.