2017-01-19 3 views
1

Скажем, у меня есть объект, в моем случае TThread, и я хочу динамически проверять одно из его свойств в функции, в этом случае TThread.Terminated.Как передать динамическое свойство в качестве параметра функции?

Есть ли чистый способ передать свойство, так что я фактически передаю функцию getter и могу ли вы проверить динамическое значение этого свойства?

Подробнее:

Мой конкретный вопрос в том, что я реализую многопоточности в Delphi впервые. Я не являюсь поклонником того факта, что стандартная реализация Delphi Threading состоит в создании собственного класса потоков для выполнения метода, который вы хотите использовать. Я нашел удовлетворительное решение этой досады, когда кто-то использует делегат для определения метода для выполнения, а затем передает фактический метод в объект потока при создании, что позволяет лучше разделить/инкапсулировать код потока и метод, который будет выполняться с помощью нить. Подробности ниже.

Однако реализация вышеуказанной идеи, которую я нашел, использует TThread.Suspend, чтобы разрешить досрочное завершение выполняемого метода. Документы Delphi отмечают TThread.Suspend как устаревшие, а вместо этого предлагают, чтобы длинные методы должны проверять свойство Terminated, чтобы разрешить досрочное расторжение при запросе родительского потока. Таким образом, мне нужен способ передать ссылку на TThread.Terminated на метод, который он выполняет, чтобы этот метод мог реализовать свое раннее завершение.

Я не могу передать свойство по ссылке, используя синтаксис var. Я не могу передать базовое поле FTerminated по ссылке, так как это частный член TThread. Я знаю, что могу просто передать ссылку на объект TThread в этот метод, но этот тип циклических ссылок кажется халатным способом обойти проблему.

Итак, есть ли «принятый» способ динамически передать свойство, или я придерживаюсь своего текущего метода?

Текущий код: интерфейс

uses 
    Windows, Messages, Classes, Forms; 

type 
    // Method Thread Will Execute. Acts on Data. Runs in RunningThread. 
    // (Reference to RunningThread is past so method can check runningthread.terminated 
    // to allow long running methods to terminate early when parent thread requests it) 
    TThreadMethod = procedure (Data: pointer; RunningThread: TThread) of object; 
    //A Simple Thread Which Excecutes a Method on Some Data 
    TSimpleThread = class(TThread) 
    protected 
     Method: TThreadMethod; 
     Data: pointer; 
     procedure Execute; override; 
    public 
     constructor Create(Method: TThreadMethod; Data: pointer; CreateSuspended: boolean); overload; 
    end; 

implementation 
    // SimpleThread 
    constructor TSimpleThread.Create(Method: TThreadMethod; 
           Data: pointer; 
           CreateSuspended: boolean); 
    begin 
    Self.Method := Method; 
    Self.Data := Data; 
    inherited Create(CreateSuspended); 
    Self.FreeOnTerminate := True; 
    end; 

    procedure TSimpleThread.Execute(); 
    begin 
    Self.Method(Data, Self); 
    end; 
+0

Попросите класс нить реализовать интерфейс, который вы передаете в свой метод выполнения потока. –

+0

- * «Я не могу передать основное поле FTerminated по ссылке, поскольку он является частным членом TThread». * - Не то, что я рекомендую, но вы можете, поскольку геттер только читает поле. См. Обсуждение в разделе [этот пост] (http://stackoverflow.com/questions/25666626/delphi-change-main-form-while-application-is-running). –

ответ

4

Скажем, у меня есть объект, в моем случае TThread, и я хочу, чтобы проверить один из его свойств динамически в функции, в данном случае TThread.Terminated ,

Лучший способ справиться с этим состоит в получении нового класса от TThread и переопределить его виртуальный Execute() метода для запуска кода нити непосредственно, а затем этот код может получить доступ к Terminated свойству объекта, когда это необходимо. Так же, как ваш пример.

Если ваш код нить не может быть поставлен в Execute() непосредственно (например, вы используете TThread.CreateAnonymousThread() или TTask вместо этого), где вы не можете передать исходный указатель TThread объект непосредственно к процедуре потока, не все потеряно. TThread обладает свойством класса CurrentThread, чтобы получить доступ к объекту TThread, который выполняет вызывающий код. Когда TThread начинает работать, он сохраняет свой указатель Self в переменной thread (поэтому каждый контекст потока получает свою собственную копию переменной), которую затем получает доступ к свойству CurrentThread.

Однако, ваш вопрос отмечен как Delphi 7, который не поддерживает анонимные процедуры, анонимные потоки или задачи, поэтому вы в основном застреваете вручную, передавая свой объект TThread в свой код процедуры. Вы можете использовать свою собственную переменную thread, если вы не хотите передавать указатель объекта в качестве параметра процедуры.

Есть ли чистый способ передать свойство, так что я фактически передаю функцию геттера и могу ли это проверить динамическое значение этого свойства?

Для начала, нет функции добытчика, то Terminated свойства направляет возвращает FTerminated член, который private в любом случае. Для считывания с объекта Terminated нужен фактический указатель объекта TThread.

Единственный способ передать указатель/ссылку на элемент FTerminated - это использовать Extended RTTI, которого нет в Delphi 7. Так что это вам не поможет. Старый RTTI в Delphi 7 недостаточно богат, чтобы получить доступ к члену FTerminated.

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

Тогда вы работаете с плохой реализацией.

Если вы не имеете в виду параметр ACreateSuspended конструктора TThread, который совершенно безопасен в использовании. Если для него установлено значение Истина, поток не создается, а затем приостанавливается, он создается в приостановленном состоянии для начала. Вы просто назовете Resume() (или Start() в более поздних версиях Delphi), когда вы будете готовы начать поток после его создания.

В противном случае просто установите ACreateSuspended в значение False и дайте автозапуск автоматически после выхода всех конструкторов.

Документы Delphi маркировать TThread.Suspend как устаревший

Это правильно. Suspend() никогда не будет безопасен для использования из-за пределов потока, поскольку другие потоки не могут гарантировать, что нить находится в безопасном состоянии, которое должно быть приостановлено. Suspend() можно использовать только в том случае, если нить приостанавливается внутри своего собственного метода Execute(), так как только этот поток будет знать, когда безопасно выполнять приостановку. Но даже в этом случае обычно существуют лучшие способы реализации приостановки потока без фактического использования Suspend() (например, ожидания на TEvent).

Таким образом, мне нужен способ передать ссылку на TThread.Terminated на метод, который он выполняет, чтобы этот метод мог реализовать свое раннее завершение.

Нет, вам необходимо передать ссылку на объект TThread на вашу процедуру, а не ссылку на какое-либо конкретное свойство.

Я не могу передать свойство ссылкой с помощью синтаксиса var.Я не могу передать базовое поле FTerminated по ссылке, так как это частный член TThread.

Исправить.

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

Это единственный способ, если вы хотите использовать свойство TThread.Terminated.

В противном случае просто не используйте TThread.Terminated. Вместо этого используйте собственный механизм сигнализации. Объект Terminated - это только флаг Boolean, не более того. Он предоставляется как удобство, а не требование. Вы можете использовать все, что хотите, чтобы сигнализировать о завершении своей процедуры, и поток для завершения.

Итак, есть ли «приемлемый» способ динамически передать свойство, или я придерживаюсь своего текущего метода?

Ваш текущий метод в основном то, что вам нужно сделать, особенно в Delphi 7. Для более поздних версий Delphi, вы в основном воссоздавать то, что TThread.CreateAnonymousThread() делает, например:

TThread.CreateAnonymousThread(
    procedure 
    begin 
    MyMethod(MyData, TThread.CurrentThread); 
    end 
).Start; 
+0

Он хочет создать собственную версию 'CreateAnonymousThread'. Прекрасно. Даже не нужно выводить из «TThread». –

+0

Почему вы думаете, что производный класс не нужен? 'TThread' не поддерживает запуск предоставленного пользователем метода в версиях Delphi, у которых нет' CreateAnonymousThread() '. –

+0

Например, вы можете использовать BeginThread. Если ты хочешь. Он просто не хочет выводить новый класс для каждой задачи. –

Смежные вопросы