2013-09-12 1 views
1

Я хотел бы подкласс TThread, чтобы иметь класс потоков, который, когда FreeOnTerminate = True, устанавливает в nil его ссылочную переменную. Другими словами, я хочу сделать что-то вроде этого:Как установить внешнюю переменную в ноль внутри метода?

TFreeAndNilThread = class(TThread) 
private 
    FReferenceObj: TFreeAndNilThread; //?? // <-- here I'm holding the reference to myself 
protected 
    procedure Execute; 
    procedure DoTerminate; override; // <-- here I'll set FReferenceObj to nil after inherited (only if FreeAndNil = True) 
public 
    constructor Create(CreateSuspended: Boolean; var ReferenceObj); reintroduce; <-- untyped param like FreeAndNil and TApplication.CreateForm ?? 
end; 

Потребитель код должен быть таким:

var 
    MyThread: TFreeAndNilThread; 

begin 
    MyThread := TFreeAndNilThread.Create(True, MyThread); 
    MyThread.FreeOnTerminate := True; 
    MyThread.Resume; 
end; 

... и тогда я мог бы безопасно проверить Assigned(MyThread).

Я вижу, как FreeAndNil удается установить ссылочный объект, пройденный нетипизированным параметром, до nil, но он делает это локально. В моем случае я должен «сохранить» его в поле класса (FReferenceObj) и сделать его ноль в другом месте (DoTerminate).

Как я могу правильно передать, сохранить и получить его? Я могу думать о передаче и хранении адреса MyThread вместо MyThread, но я надеюсь, что есть более элегантный способ.

Спасибо!

+1

Должно быть, что-то не хватает. Что не так с подключением 'OnTerminate' и установкой' MyThread: = nil'? (или удаление записи из списка потоков, если на то пошло) –

+0

OnTerminate выполняется в основном потоке, потому что называется синхронным (см. стандартную реализацию 'DoTerminate'. – yankee

+0

@ yankee. Но вам нужно убедиться, что код, который устанавливает ссылку на 'nil', выполняется в том же потоке, который обращается к этой ссылке. –

ответ

1

Вам нужно сохранить указатель на переменную.

type 
    TFreeAndNilThread = class(TThread) 
    private 
    FReferenceObj: ^TFreeAndNilThread; 
    end; 

Затем вам нужно передать в ссылке в конструкторе:

type 
    TFreeAndNilThread = class(TThread) 
    private 
    FReferenceObj: ^TFreeAndNilThread; 
    public 
    constructor Create(var ReferenceObj: TFreeAndNilThread); 
    end; 

Затем вы можете реализовать это следующим образом:

constructor TFreeAndNilThread.Create(var ReferenceObj: TFreeAndNilThread); 
begin 
    inherited Create(True); 
    FreeOnTerminate := True; 
    FReferenceObj := @ReferenceObj; 
end; 

Когда поток умирает вы установите ссылку nil следующим образом:

ReferenceObj^ := nil; 

Для удобства, поскольку вы, скорее всего, относитесь к этому как к базовому классу, из которого вы получаете другие классы, вы можете предпочесть сделать этот параметр конструктором нетипизированным. Вы можете просто сделать это, не внося никаких изменений в код. Когда вы сделаете это конструктор выглядит следующим образом:

constructor Create(var ReferenceObj); 

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

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

+0

Большое спасибо. Что касается состояния гонки, тест для 'Assigned (MyThread)' выполняется в основном потоке, чтобы убедиться, что 'MyThread' завершил выполнение: после этого теста я не использую' MyThread', поэтому (если я не отсутствует что-то), не должно быть состояния гонки. – yankee

+0

В этом случае вы можете просто сделать то, что Хью предложил мне поверить. –

+0

Возможно, вы правы в том, что первоначальная идея не является реальным решением, но я хочу сказать, что 1) Ожидание завершения потока с функцией ожидания и 2) Обработка события OnTerminate является взаимоисключающим, поскольку в противном случае обеспечивается взаимоблокировка. Я хотел бы дождаться завершения потока таким образом, чтобы обеспечить a) таймаут, b) не замораживать графический интерфейс и c) выполнение OnTerminate, если оно реализовано, но не хотят принудительно создавать такой обработчик, если не нужно. Поэтому я представил себе нечто вроде '(Assigned (MyThread)) и (не TimeoutExpired). Application.ProcessMessages;' – yankee