Как вызвать методы потока из потока?

Я только что узнал, что создание и использование Vcl.TTimer из рабочего потока не является потокобезопасным. Мой рабочий поток работает нормально «большую часть времени», используя Vcl.TTimer. Я нашел класс TTimerThread, основанный на событиях, который тоже работает нормально, но я не знаю, как объединить оба класса, которые TTimerThread имеют доступ к методам моего рабочего потока.

ExecuteTimed() будет вызван при возникновении события (3000 мс).

Псевдокод:

uses TimerThread;

type
  TMyTimerThread = class( TTimerThread )
  protected
  procedure ExecuteTimed; override;
[...]



TMQTTClient = class(TThread)
private
  [...]
  fKeepAliveTimer:TTimer;                     // Works, but is ugly.
  fKeepAliveTimerThread:TMyTimerThread;       //  
  [...]


procedure TMyTimerThread.ExecuteTimed;
begin
  beep;   // Works fine!
end;

[...]

constructor TMQTTClient.Create(aHostname: string; aPort: integer; ClientID:String;aKeepAliveSeconds : word);
begin
  //init some stuff...
  fKeepAliveTimer:=TTimer.Create(nil); // ugly
  fKeepAliveTimer.Interval:=fKeepAliveSeconds * 1000;
  fKeepAliveTimer.OnTimer:=DoKeepAlive; // The function pointer which should be called ~ every xxxx ms. Works fine, but no thread safe.

  fKeepAliveTimerThread:=TMyTimerThread.Create();
  fKeepAliveTimerThread.Interval:=3000;
  //fKeepAliveTimerThread.ExecuteTimed := ?; //here I wish to assign a pointer to the ExecuteTimed method of MyTimerThread.

Я мог бы представить решение этой проблемы, используя TThread.Synchronize() в ExecuteTimed(), который вызывает экземпляр рабочего потока, например frmMain.myMqttWorkerClass.DoKeepAlive, но я сомневаюсь, что это правильный путь.

Возможно, вам следует немного объяснить вашу общую цель. Я не знаком с TTimerThread, но думаю, можно предположить, что ExecuteTimed и Execute вашего TMQTTClient потока являются разными потоками, поэтому, если вы хотите получить доступ к одним и тем же структурам данных из обоих, вам понадобится какая-то блокировка синхронизации, например. критические разделы, чтобы защитить доступ к общим ресурсам.

500 - Internal Server Error 06.08.2024 19:34
TThread.Synchronize() фактически используется для выполнения кода в основном потоке.
Stas Simonov 06.08.2024 19:43
TTimerThread действительно имеет вызов criticalSection, и это работает. Это скорее базовый вопрос о том, как определить TMqttClient, что событие TTimerThread произошло.
Sascha Ott 06.08.2024 19:45

Вы рассматривали TThread.Sleep метод или GetTime опрос?

Stas Simonov 06.08.2024 19:51

@Stas Нет. Sleep(60000) и опросы тоже кажутся неприятными.

Sascha Ott 06.08.2024 19:55

Подробно рабочий поток обрабатывает связь по протоколу mqtt. Протоколу необходимо отправлять сообщение поддержания активности каждые xx секунд. Ранее я решил это с помощью TTimer, который, как я узнал, не является потокобезопасным. Просто хочу сделать это лучше

Sascha Ott 06.08.2024 20:00
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
6
54
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы можете добавить публичное событие в TMyTimerThread и TMQTTClient назначить ему обработчик DoKeepAlive, точно так же, как вы это сделали с TTimer, например:

type
  TMyTimerThread = class(TTimerThread)
  private
    fOnTimer: TNotifyEvent;
  protected
    procedure ExecuteTimed; override;
  public
    property OnTimer: TNotifyEvent read fOnTimer write fOnTimer;
  end;

  TMQTTClient = class(TThread)
  private
    [...]
    fKeepAliveTimerThread: TMyTimerThread;
    procedure DoKeepAlive(Sender: TObject);
    [...]
  end;

...

procedure TMyTimerThread.ExecuteTimed;
begin
  if Assigned(fOnTimer) then fOnTimer(Self);
end;

...

constructor TMQTTClient.Create(aHostname: string; aPort: integer; ClientID:String;aKeepAliveSeconds : word);
begin
  //init some stuff...
  fKeepAliveTimerThread := TMyTimerThread.Create();
  fKeepAliveTimerThread.Interval := 3000;
  fKeepAliveTimerThread.OnTimer := DoKeepAlive;
end;

Просто имейте в виду, что DoKeepAlive будет вызываться в контексте потока таймера, поэтому убедитесь, что все, что он делает, является потокобезопасным.

Да, использование TNotifyEvent кажется правильным. DoKeepAlive() просто проверяет, подключен ли TCPClient.Connected, выполняет некоторые действия в ТБ и, наконец, вызывает FTCPClient.IOHandler.write(). Думаю, это безопасно

Sascha Ott 06.08.2024 20:11

Работает как шарм! :) Спасибо, Реми!

Sascha Ott 06.08.2024 20:21

@SaschaOtt Connected выполняет операцию чтения, поэтому с ней нужно быть очень осторожным, если у вас есть другие потоки, которые также читают из того же сокета. То же самое и с Write, если уж на то пошло. Поток может безопасно записывать, пока другой поток читает, но если 2+ потока выполняют чтение или 2+ потока выполняют запись, вам необходимо соответствующим образом синхронизировать доступ к сокету. Если TMQTTClient.Execute что-то пишет, то я бы предложил позволить TMQTTClient.Execute выполнять ВСЕ операции чтения и записи. Попросите DoKeepAlive поместить уведомление в очередь, которая TMQTTClient.Execute опрашивает и записывает при необходимости.

Remy Lebeau 06.08.2024 20:38

Действительно TMQTTClient.Execute пишет/читает в/из сокета. Фактически все операции чтения/записи выполняются там с использованием сигнальной переменной. За исключением сообщения о сохранении активности, которое запускается таймером. Спасибо за подсказку.

Sascha Ott 06.08.2024 20:59

@SaschaOtt Тогда может иметь смысл, чтобы таймер сигнализировал о другой переменной, которая Execute просматривает, а затем записывает сообщение о сохранении активности при получении сигнала

Remy Lebeau 06.08.2024 21:01

Другие вопросы по теме