Расширить список функций обратного вызова

Страницы: 1
RSS
Расширить список функций обратного вызова
 
Не помню, предлагали уже такие пожелания или нет.

1) Добавить функцию обратного вызова OnTimer(), которая будет вызываться терминалом с некоторой периодичностью. Надо понять, как указывать период срабатывания. Конкретно для моих задач хватит срабатывания раз в секунду, но мало ли кому чаще надо. Предлагаю обсудить, кому интересно.

Пускай первый раз функция OnTimer() запускается сразу после OnInit() и возвращает число число миллисекунд, через которое будет сделан следующий вызов функции OnTimer(). Если возвращается не число, то пусть следующий вызов будет через 1 секунду.

Реализация на стороне терминала кажется несложной.

2) Дать пользователю возможность из main-потока подать команду на запуск своей функции в потоке коллбэков. Нормальное название не придумал, но надо что-то типа
Код
 executeAsCallback(function() ... end)

В принципе, имея п.2, пользователь сможет самостоятельно реализовать п.2, если только в main-потоке не выполняются тяжёлые вычислительные задачи.

Надо всё это для того, чтобы можно было выполнять в потоке коллбэков регулярные задачи, которые удобнее и архитектурно правильнее делать там, а не в main.
 
1) Вам main() зачем дадена?
Код
main()
   while run do
      sleep(1000)
      DoMyFunc()   -- вот и "таймер" раз в секунду
   end
end


2) А зачем? что плохого в выполнении в нужного кода опять же в main() ?
 
1) OnTimer прекрасно эмулируется в цикле со sleep, там же указывается период срабатывания (у меня их даже два: раз в полторы секунды и раз в 15 секунд). А никакого OnInit вообще нет.

2) С потоками разработчики вообще перемудрили - нельзя туда пускать юзера вообще, не его это свинячье дело, в каком потоке что выполнять. И так уже управление теряется между потоками, а коллбеки способны подвесить Квик!
 
Пункт 2 я предлагал в несколько другой редакции, сейчас передумал. Причина: единственное ожидаемое применение - обойти зависание при работе с таблицами, пока квик ждет завершения мейна. Но сам заказ колбека придется посылать той же SendMessage (а как иначе), в результате вместо зависания на таблице зависнем на отправке колбека.

Пункт 1 по-моему так должен формулироваться
Код
BOOLEAN setTimer(INTEGER timeout_ms, FUNCTION callback)
и прототип колбека
Код
INTEGER CALLBACK timerCallback(VOID)
Если колбек возвращает отрицательное число, данный таймер прибивается. Если ноль или больше - меняется период таймера на возвращенное значение. Интересный крайний случай при периоде 0 - это тот же немедленный вызов колбека в потоке квика.
 
Антон, спасибо за предложенные варианты по п.1. Опасения по поводу п.2 понял. Получается, что лучше просить разработчиков реализовать п.1.

swerg, я же не просто так написал, что
Цитата
Надо всё это для того, чтобы можно было выполнять в потоке коллбэков  регулярные задачи, которые удобнее и архитектурно правильнее делать там,  а не в main.
 
Цитата
Anton написал:
SendMessage (а как иначе)

Через PostMessage, конечно
Кстати, 1) есть у меня (ну почти, сделал, не подумал зачем это может понадобится, выкинул, но восстановить не сложно)
 
Цитата
_sk_ написал:
Надо всё это для того, чтобы можно было выполнять в потоке коллбэков  регулярные задачи, которые удобнее и архитектурно правильнее делать там,  а не в main.

Архитектурно правильно (в тех рамках, что мы имеем) как можно меньше нагружать колбеки основного потока, вынося всё что возможно в main(). Некоторые еще и несколько потоков делают, этакие множественные main(). Или даже в отдельный процесс.
А потому всё же хотелось бы услышать что это за такие "архитектурно правильные задачи", которые нельзя вынести в main().
 
Цитата
swerg написал:
Кстати, 1) есть у меня

Сорри, имелось ввиду 2) есть у меня
 
Есть список слушателей, которых надо уведомлять о коллбэках (каждого слушателя о своём подмножестве коллбэков) как можно быстрее. При этом в main() время от времени возникают вычислительно тяжёлые задачи (заранее нельзя контролировать время их выполнения и разбивать на мелкие подзадачи).

Если пробрасывать события из потока коллбэков в поток main(), то будут возникать задержки, чего не стоит допускать.

Есть потребность в отключении слушателей по таймауту (а не когда придёт очередной коллбэк) и подключении новых вне зависимости от приходящих коллбэков. При этом итерирование по таблице с добавлением/удалением сразу в двух потоках без должной синхронизации как-то плохо выглядит.

Выходит, что удобно иметь возможность инициировать какой-то коллбэк из потока main, не надо думать про синхронизацию. Пункты 1 и 2 были обсуждением таких возможностей.
 
Костыли типа отправки какой-нибудь транзакции на сервер, чтобы OnTransReply сработал, не предлагать.
 
Цитата
_sk_ написал:
Есть потребность в отключении слушателей по таймауту (а не когда придёт очередной коллбэк)

"Отключить слушателей по таймауту (а не когда придёт очередной коллбэк)" абсолютно эквивалентно "в начале колбека проверить надо ли уведомлять этого конкретного слушателя или таймаут его уведомлений истёк".      В целом вся эта машинерия выглядит через чур запутанной, потом же сами концов в ней не найдёте... "Почему этот слушатель не сработал? то ли по тайм-ауту отключили, то ли колбека не было, то ли просто что-то пошло не так." "Архитектурно-правильно".... но это так, мысли вслух
 
Цитата
_sk_ написал:
Костыли типа отправки какой-нибудь транзакции на сервер, чтобы OnTransReply сработал, не предлагать.
А если без сетевого обмена костылик? Поглядите
Код
local run = true
local tid = nil

function main()
   tid = AllocTable()
   AddColumn(tid, 1, '1', true, QTABLE_INT_TYPE, 1)
   CreateWindow(tid)
   InsertRow(tid, -1)
   InsertRow(tid, -1)
   SetWindowPos(tid, 0, 0, -1000, -1000)
   SetSelectedRow(tid, 1)
   SetTableNotificationCallback(tid, function(t, m, w, l)
      if QTABLE_SELCHANGED == m then
         if 2 == w then
            SetSelectedRow(t, 1)
            message('!')
         end
      end
   end)
   while run do
      sleep(1000)
      SetSelectedRow(tid, 2)
   end
   OnStop()
end

function OnStop()
   local t = tid
   tid = nil
   if t then
      DestroyTable(t)
   end
   run = false
end
Создаем окошко с двумя строчками, выделяем первую, ставим колбек на изменение выделения, в колбеке возвращаем выделение в первую строку. Когда надо колбек выполнить в квиковском потоке, меняем выделение на вторую строку и вуаля, тут же отрабатывает колбек, чего мы и хотели. Вместо message(), естественно, вызываем что-то свое полезное.
 
Идея прикольная :) Осталось окно скрипта куда-нибудь спрятать и будет "зачётный костыль".

Терминал является уже достаточно сложной системой, чтобы в нём были такого рода "дыры", которыми можно пользоваться в благих целях.

Ладно, а разработчики могут нам что-то человеческое предоставить?

P.S. Вот было бы здорово создать реестр подобного рода маленьких хитростей. А то я, кажется, только
Код
table.ssort({ 0, 1 }, function(a, b)
Ваш код здесь
return true
end)

для синхронизации знаю.
 
Цитата
_sk_ написал:
Осталось окно скрипта куда-нибудь спрятать
Если полосы прокрутки выключены, можно спрятать в позицию (-1000, -1000), со включенными не придумал куда. С помощью длл можно скрыть, но тогда можно и совсем без окна обойтись.
 
Цитата
Владимир написал:
1) OnTimer прекрасно эмулируется в цикле со sleep, там же указывается период срабатывания (у меня их даже два: раз в полторы секунды и раз в 15 секунд). А никакого OnInit вообще нет.
Подскажите, а как вам удается два цикла со sleep сделать? Вы внутри main их делаете? или внутри какого-то колбека?

Я тоже использую sleep в main, но насколько я исследовал этот вопрос, когда идет функция sleep он полностью прекращает свою работу и просто ждет это время
 
Sergey Denegin, Элементарно, Господи! И не нужно никаких "два цикла со sleep". Вот кусок main:
Код
while f do -- бесконечный цикл до остановки скрипта
 if fm>3 then DestroyTable(t);fm=0;end;
 if fm==3 then DestroyTable(t);fm=1;end;
 if fm==1 then u();fm=2;end;   -- рисуем или убираем всплывающее меню
 r();-- раз в 1.5 секунды запускаем утилиту опроса
sleep(1500);   -- текущих данных и принятия решений по ним
end;   -- конец бесконечного цикла
end;   -- конец функции main()

А вот эмулятор обработчика:
Код
function r()   -- мелкий обработчик прерывания по таймеру
 c=c+1;      -- счётчик прерываний
 if c==10 then c=0; R();end;   -- прошло 15 секунд, включаем анализ
...
КТО ИМЕННО "полностью прекращает свою работу и просто ждет это время"? Скрипт? Так это просто интерпретируемый текст - он вообще никогда не работает.  :smile:  
 
Цитата
_sk_ написал:
Получается, что лучше просить разработчиков реализовать п.1.
Цитата
_sk_ написал:
разработчики могут нам что-то человеческое предоставить?
А давайте все дружно позовем деда Мороза!


Цитата
Anton написал:
Код
    SetTableNotificationCallback (tid,  function (t, m, w, l)
       if  QTABLE_SELCHANGED  ==  m  then 
          if 2 == w then 
             SetSelectedRow (t, 1)
             message ( '!' )
          end 
       end 
    end)
Допустимо ли использование SetSelectedRow внутри колбека QTABLE_SELCHANGED, с учётом, что SetSelectedRow - синхронное сообщение?
Я не могу быть заинтересован в устранении ошибок в чужом ПО больше, чем его разработчик.
 
Цитата
Старатель написал:
с учётом, что SetSelectedRow - синхронное сообщение?
В данном случае да, колбек дернут из потока квика, SendMessage отправляется из потока квика в поток квика, а это просто прямой вызов оконной процедуры.
 
Цитата
Anton написал:
Когда надо колбек выполнить в квиковском потоке, меняем выделение на вторую строку и вуаля, тут же отрабатывает колбек, чего мы и хотели.

Кста, main будет ждать завершения колбека, поэтому результат его работы сразу можно использовать в main.
Скрытый текст

Хотелось бы что-то типа такого "из коробки" без необходимости дёргать GUI.
Я не могу быть заинтересован в устранении ошибок в чужом ПО больше, чем его разработчик.
 
Ну если уж просить, почему бы не попросить еще один поток для своих колбеков. Т.е. реализовать методы регистрации своих функций обратного вызова и отдельный поток для них, чтобы не лезть в основной поток вовсе.
 
Цитата
Nikolay написал:
попросить еще один поток для своих колбеков
Отдельный поток у вас уже есть - main. Зачем ещё один городить?

Цитата
Nikolay написал:
чтобы не лезть в основной поток вовсе.
Дело в том, что вы так или иначе всё равно лезете в основной поток: GUI, хранилище данных.
Я не могу быть заинтересован в устранении ошибок в чужом ПО больше, чем его разработчик.
 
Цитата
Nikolay написал:
Ну если уж просить, почему бы не попросить еще один поток для своих колбеков. Т.е. реализовать методы регистрации своих функций обратного вызова и отдельный поток для них, чтобы не лезть в основной поток вовсе.
  То есть, это то что, реализовано в OS_Quesha.
У меня есть гипотеза, почему это, до сих пор. не доступно для многих пользователей QUIK, но объявлять это не корректно.
 
Цитата
Старатель написал:
Отдельный поток у вас уже есть - main. Зачем ещё один городить?

Дело в том, что вы так или иначе всё равно лезете в основной поток: GUI, хранилище данных.
Да, но зачем нагружать поток терминала, пусть себе в сторонке что-то делается. Тогда можно было бы и нагруженные модули писать. Сейчас же в основном потоке только данные заполнить в общей области видимости, никаких ожиданий и долгих вычислений.
 
Цитата
Nikolay написал:
Да, но зачем нагружать поток терминала, пусть себе в сторонке что-то делается. Тогда можно было бы и нагруженные модули писать. Сейчас же в основном потоке только данные заполнить в общей области видимости, никаких ожиданий и долгих вычислений.
    Справедливое замечание.
 Конечно, между колбеками и функцией main, следует создавать очереди, которые потокобезопасны,  но кто это понимает?
 Большинство из тех, кто заходит на этот сайт, не проффесоналы.
 
Цитата
Nikolay написал:
зачем нагружать поток терминала
Кто сказал "нагружать"? Задачи на 0.1-2 мс, без фанатизма.
Я даже не понимаю тех, кто в стремлении "разгрузить" основной поток перекидывает простейшие задачи (с временем выполнения менее 2 мс) из колбеков в main (что опять же не бесплатно в плане нагрузки), создавая километровые очереди и забывая об общей производительности.
Я не могу быть заинтересован в устранении ошибок в чужом ПО больше, чем его разработчик.
 
Цитата
Старатель написал:
Хотелось бы что-то типа такого "из коробки" без необходимости дёргать GUI.
Об чем вся и ветка. Так-то задача на десять строк, если говорить о монолитной аппе. Но qlua в основную аппу не зашит, он плагин, так что на практике может быть довольно геморройно это сделать.
 
Цитата
Старатель написал:
Кто сказал "нагружать"? Задачи на 0.1-2 мс, без фанатизма.Я даже не понимаю тех, кто в стремлении "разгрузить" основной поток перекидывает простейшие задачи (с временем выполнения менее 2 мс) из колбеков в main (что опять же не бесплатно в плане нагрузки), создавая километровые очереди и забывая об общей производительности.
 С написанным можно бы согласиться, но с учетом следующих моментов:
1) байт-коды основного потока QUIK и пользовательского потока main выполняются последовательно и пока выполняется один, другой ждет;  то есть использование основного потока для выполнения функций пользователя параллелизм  добавляет только при выполнении C-функций;
2) если у пользователя запущено несколько скриптов, то пока основной поток QUIK выполняется в одном из скриптов, он перестает обслуживать другие скрипты пользователя (в том числе, и созданные им таблицы QUIK);
3) вами выложены в ветке «Кривые ошибки в QLUA» очень эффективные потокобезопасные функции работы с очередями  без синхронизации между основным потоком QUIK и потоком main;
4) перенос коротких задач пользователя в колбеки делает скрипт пользователя многопоточным, то есть, при этом пользователю нужно следить за тем, нет ли в  его скрипте проблем синхронизации.
 
Цитата
_sk_ написал:
Добавить функцию обратного вызова OnTimer(), которая будет вызываться терминалом с некоторой периодичностью

 В данном комментарии выложен модуль работы с таймерными событиями в отдельном потоке и шаблон его использования.
----
API работы с таймерными событиями:
1) Start_Timer()  -  запуск отдельного  потока обработки таймерных событий.
2) <Ключ события> = Set_Timer  (
{<Имя события>, <Интервал в млсек.>, <Функция обработки события>,{<Список парвметров функции>}} )    - запрос таймерного события.
Через <Интервал в млсек.> будет запущена в отдельном потоке <Функция обработки события> со <Списоком парвметров функции>.
3) delete_Timer (<Ключ события>) – удаление при необходимости запрошенного собы-тия.
----
Модуль и две dll (service53.dll и service54.dll) , скачанные по ссылке:
https://cloud.mail.ru/public/i3Y7/kQHBvs7um  необходимо положить в папку хранения info.exe.
---
Модуль OnTimer (для QLua 5.3 и 5.4):
Код
-- Модуль таймерных событий OnTimer ---
    -- Подключение C-пакетов service53, service54: https://cloud.mail.ru/public/i3Y7/kQHBvs7um
local WorkingFolder = getWorkingFolder()   
package.cpath = package.cpath .. ';' .. WorkingFolder .. '\\?.dll' .. ';' .. WorkingFolder .. '\\?53.dll'.. ';' .. WorkingFolder .. '\\?54.dll' .. ';' .. WorkingFolder .. '\\?51.dll'
                            .. ';' .. WorkingFolder .. '\\?3.dll'      -- C - пакеты  ----
if _VERSION == 'Lua 5.3' then   
    require('service53')     -- Подключает библиотеку service.dll, расположенную в корневом каталоге терминала QUIK (там где info.exe)   ----
else
    require('service54')  
end   
local GetMilliseconds, Start_thread  =   service.GetMilliseconds, service.Start_thread

  --- Функции работы с очередями  ---
    local new  =   function  ()
        return  {first  =   1 , last  =   0 }
    end 
    ---
    local push  =   function  (self, v)
        local  last  =  self.last  +  1 
        self[last]  =  v
        self.last  =  last
        return  last
    end 
    ---
    local pop  =   function  (self)
       local  first  =  self.first
       if  first  >  self.last  then   return   nil   end 
       local  v  =  self [first]
       self.first  =  first + 1 
       self[first]  =   nil 
       return  v, first
     end 
--------- Создание очереди ------
 local  Queue_Timer = new()  --- Таймерные события -----
 local  Queue_Timer_Sl = new()  --- Очередь отмены событий ---
-------------------------------------------
    --- Заказ таймерных событий ----
    --- Таймерное событие:  {<Имя события>, <Интервал в млсек.>, <Функция обработки события>,{<Список парвметров функции>}}
    local function Set_Timer  (self)
       if type(self) == 'table' then 
         if #self >= 3 then
             self [2] = self [2] + GetMilliseconds() 
              return push (Queue_Timer, self)
         else
            message ( '!!! Ошибка. Должно быть:  {<Имя события>, <Интервал в млсек.>, <Функция обработки события>,{<Список парвметров функции>}}') 
         end
      else
         message ( '!!! Ошибка (параметр не таблица). Должно быть:  {<Имя события>, <Интервал в млсек.>, <Функция обработки события>,{<Список парвметров функции>}}') 
      end
    end 
   -----
    local function delete_Timer  (kl)
       push (Queue_Timer_Sl, kl)
    end 
    ------
    local function Timer ()
       local ms, first_q 
       while IsRun   do 
            ---  Обработка удалений событий ---
            ms, first_q = pop (Queue_Timer_Sl) 
            while ms  do
             if Queue_Timer [ms] then Queue_Timer [ms] = nil end
            ms, first_q = pop (Queue_Timer_Sl)    
         end
         ---  Чтение очереди заказанных событий[ -----   
          for k, v in next, Queue_Timer do
            if type(k) == 'number' then
                if v [2] <= GetMilliseconds()   then
                   Queue_Timer [k] = nil
                        if v[4] then
                           pcall ( v[3], table.unpack (v[4]))
                  else
                     pcall ( v[3])  -- функция без параметров ---
                  end
               end
            end
              end
         -------------------------------------------------------
            sleep(2)      
       end
    end 
---------------------------------------
    local function Start_Timer ()
     Start_thread (Timer)
    end
  ------
  return {Set_Timer = Set_Timer, delete_Timer = delete_Timer, Start_Timer = Start_Timer }

---------------------------------------------

Шаблон использования модуля:
Код
-- Шаблон использования модуля таймерных событий
local OnTi mer = require('OnTimer')  -- Подключение модуля обработки таймерных событий  ---
local Set_Timer = OnTimer.Set_Timer
local delete_Timer = OnTimer.delete_Timer
local Start_Timer = OnTimer.Start_Timer

IsRun = true

    local function call_Timer  (kl)
        message ( 'call_Timer = ' .. kl)
    end 
   
    local function call_Timer1  ()
        message ( 'call_Timer1  () ==============')
    end 

function main()
    Start_Timer()
   local kl
    --- Заказ таймерных событий ----
    --- Таймерное событие:  {<Имя события>, <Интервал в млсек.>, <Функция обработки события>,{<Список парвметров функции>}}
   kl = Set_Timer({'Set_Timer', 100, call_Timer, {100}})
   kl = Set_Timer({'Set_Timer', 1000, call_Timer, {1000}})
   kl = Set_Timer({'Set_Timer', 5000, call_Timer, {5000}})
   kl = Set_Timer({'Set_Timer', 3000, call_Timer1})
--   delete_Timer (kl)
   
    -------------------
    while IsRun do 
   
       ----  Обработка в main остального -----
       kl = Set_Timer({'Set_Timer', 1000, call_Timer, {1000}})
        delete_Timer (kl)
       ----  Конец Обработка в main остального -----
        sleep(500)        
  end
  ------------------
end   
   
function OnStop()
     IsRun = false
    DestroyTable(TableQUIK)
     DestroyTable(TableQUIK1)
    return 10000   --  !!  По истечении этого интервала времени (в млсек.), данного скрипту на завершение работы, функция main() завершается принудительно. 
                                -- При этом возможна потеря системных ресурсов.   ! Если функция не выдает значение, то по умолчанию оно 5000 (5 сек.)
end
 
Цитата
TGB написал:
использование основного потока для выполнения функций пользователя параллелизм  добавляет только при выполнении C-функций;
На "параллелизм" я и не рассчитываю.


Цитата
TGB написал:
если у пользователя запущено несколько скриптов, то пока основной поток QUIK выполняется в одном из скриптов, он перестает обслуживать другие скрипты пользователя (в том числе, и созданные им таблицы QUIK)
Таблицы обслуживаются только в одном основном потоке. Доступ к данным - тоже только в одном потоке.
Так что все ваши "несколько скриптов" дружно встают в очередь, как только им понадобится что-то вывести в таблицу. И включаются в общую конкурсную массу вмести с другими таблицами и графиками.


Цитата
TGB написал:
перенос коротких задач пользователя в колбеки делает скрипт пользователя многопоточным, то есть, при этом пользователю нужно следить за тем, нет ли в  его скрипте проблем синхронизации

В данной ветке, наоборот, обсуждение, переноса задачи, выполняемой по таймеру, в основной поток, чтобы не заморачиваться синхронизацией:
Цитата
_sk_ написал:
удобно иметь возможность инициировать какой-то коллбэк из потока main, не надо думать про синхронизацию
Я не могу быть заинтересован в устранении ошибок в чужом ПО больше, чем его разработчик.
 
Цитата
Старатель написал:
Цитата TGB  написал: перенос коротких задач пользователя в колбеки делает скрипт пользователя многопоточным, то есть, при этом пользователю нужно следить за тем, нет ли в  его скрипте проблем синхронизации.

 В данной ветке, наоборот, обсуждение, переноса задачи, выполняемой по таймеру, в основной поток, чтобы не заморачиваться синхронизацией:
Цитата _sk_  написал: удобно иметь возможность инициировать какой-то коллбэк из потока main, не надо думать про синхронизацию

 Из выше приведенного я понял, что о синхронизации мною было написано слишком лапидарно и не очень понятно.
Попробую пояснить это более детально.
   Проблемы синхронизации возникают, только в случае, если у двух или более потоков есть общие модифицируемые при обработке данные. Поэтому при обработке колбеков, до тех пор, пока в main есть только цикл со sleep, то синхронизацией можно не озадачиваться. Но как только, после очередного внесения изменений  в скрип, появятся общие для функций колбеков и функций main модифицируемые данные, с синхронизацией следует разбираться. Причем,  проблемы синхронизации возникают не в отдельных потоках, а при их взаимодействии.
   Конечно, можно всю функциональность скрипта перенести в основной (единственный) поток QUIK и не будет никаких проблем с синхронизацией. Надо только следить за тем, чтобы при изменении скрипта не появились общие (для main и основного потокаQUIK) модифицируемые данные.
 
Нужно ли переносить функциональность скрипта в основной поток с учетом написанного мною в предыдущем комментарии, а также с учетом цитируемого ниже?
Цитата
TGB написал:
Таблицы обслуживаются только в одном основном потоке. Доступ к данным - тоже только в одном потоке.Так что все ваши "несколько скриптов" дружно встают в очередь, как только им понадобится что-то вывести в таблицу. И включаются в общую конкурсную массу вмести с другими таблицами и графиками.
   Ответ разработчика QUIK (документ «Использование Lua в Рабочем месте QUIK»):
«При использовании событийной модели Lua скрипт выполняется в двух потоках: функции обратного вызова выполняются в основном потоке РМ QUIK, а функция main() в до-полнительном потоке РМ QUIK (подробнее см. п. 1). При этом для предотвращения «подвисаний» РМ QUIK необходимо каким-либо образом оптимизировать сценарии, описан-ные в функциях обратного вызова. Одним из способов такой оптимизации является перенос логики обработки полученных сигналов в функцию main(). Данный подход сводит количество сценариев в функции обратного вызова до одного, а именно добавление в глобальную Lua таблицу (очередь) записи о том, что функция сработала и вернула определённые значения. Таким образом, мы получаем очередь событий, которые необходимо обработать в другом потоке.»
-----
  Если кому интересно, то в моем комментарии https://forum.quik.ru/messages/forum10/message57686/topic6198/#message57686
выложен код реализации модуля обработки событий колбеков и событий таблиц QUIK с использованием очередей.  Там же приведен скрипт-шаблон подключения модуля и при-мер его использования.
 При использовании очередей проблем синхронизации нет вообще. Основной поток выполняет по событиям QUIK только короткие записи параметров колбеков в очереди, которые обрабатываются в потоке main.
 
Извините не заметил искажения автора цитаты. В предыдущем комментарии цитата Старателя
 
Цитата
TGB написал:
Цитата
_sk_ написал:
Добавить функцию обратного вызова OnTimer(), которая будет вызываться терминалом с некоторой периодичностью

 В данном комментарии выложен модуль работы с таймерными событиями в отдельном потоке и шаблон его использования.
----
API работы с таймерными событиями:
1) Start_Timer()  -  запуск отдельного  потока обработки таймерных событий.
2) <Ключ события> = Set_Timer  (
{<Имя события>, <Интервал в млсек.>, <Функция обработки события>,{<Список парвметров функции>}} )    - запрос таймерного события.
Через <Интервал в млсек.> будет запущена в отдельном потоке <Функция обработки события> со <Списоком парвметров функции>.
3) delete_Timer (<Ключ события>) – удаление при необходимости запрошенного собы-тия.
----
Модуль и две dll (service53.dll и service54.dll) , скачанные по ссылке:
https://cloud.mail.ru/public/i3Y7/kQHBvs7um   необходимо положить в папку хранения info.exe.
---
Модуль OnTimer (для QLua 5.3 и 5.4):
Код
   -- Модуль таймерных событий OnTimer --- 
     -- Подключение C-пакетов service53, service54: https://cloud.mail.ru/public/i3Y7/kQHBvs7um 
 local  WorkingFolder  =   getWorkingFolder ()   
package.cpath  =  package.cpath  ..   ';'   ..  WorkingFolder  ..   '\\?.dll'   ..   ';'   ..  WorkingFolder  ..   '\\?53.dll'  ..   ';'   ..  WorkingFolder  ..   '\\?54.dll'   ..   ';'   ..  WorkingFolder  ..   '\\?51.dll' 
                             ..   ';'   ..  WorkingFolder  ..   '\\?3.dll'        -- C - пакеты  ---- 
 if  _VERSION  =  =   'Lua 5.3'   then    
     require ( 'service53' )      -- Подключает библиотеку service.dll, расположенную в корневом каталоге терминала QUIK (там где info.exe)   ---- 
 else 
     require ( 'service54' )  
 end    
 local  GetMilliseconds, Start_thread   =    service.GetMilliseconds, service.Start_thread

   --- Функции работы с очередями  --- 
     local  new   =     function   ()
         return   {first   =     1  , last   =     0  }
     end  
     --- 
     local  push   =     function   (self, v)
         local   last   =   self.last   +    1  
        self[last]   =   v
        self.last   =   last
         return   last
     end  
     --- 
     local  pop   =     function   (self)
        local   first   =   self.first
        if   first   >   self.last   then     return     nil     end  
        local   v   =   self [first]
       self.first   =   first  +   1  
       self[first]   =     nil  
        return   v, first
      end  
 --------- Создание очереди ------ 
  local   Queue_Timer  =  new()   --- Таймерные события ----- 
  local   Queue_Timer_Sl  =  new()   --- Очередь отмены событий --- 
 ------------------------------------------- 
     --- Заказ таймерных событий ---- 
     --- Таймерное событие:  {<Имя события>, <Интервал в млсек.>, <Функция обработки события>,{<Список парвметров функции>}} 
     local   function  Set_Timer  (self)
        if  type(self)  =  =   'table'   then  
          if   # self  >  =   3   then 
             self [ 2 ]  =  self [ 2 ]  +  GetMilliseconds() 
               return  push (Queue_Timer, self)
          else 
             message  (  '!!! Ошибка. Должно быть:  {<Имя события>, <Интервал в млсек.>, <Функция обработки события>,{<Список парвметров функции>}}' ) 
          end 
       else 
          message  (  '!!! Ошибка (параметр не таблица). Должно быть:  {<Имя события>, <Интервал в млсек.>, <Функция обработки события>,{<Список парвметров функции>}}' ) 
       end 
     end  
    ----- 
     local   function  delete_Timer  (kl)
       push (Queue_Timer_Sl, kl)
     end  
     ------ 
     local   function  Timer ()
        local  ms, first_q 
        while  IsRun    do  
             ---  Обработка удалений событий --- 
            ms, first_q  =  pop (Queue_Timer_Sl) 
             while  ms   do 
              if  Queue_Timer [ms]  then  Queue_Timer [ms]  =   nil   end 
            ms, first_q  =  pop (Queue_Timer_Sl)    
          end 
          ---  Чтение очереди заказанных событий[ -----    
           for  k, v  in  next, Queue_Timer  do 
             if  type(k)  =  =   'number'   then 
                 if  v [ 2 ]  <  =  GetMilliseconds()    then 
                   Queue_Timer [k]  =   nil 
                         if  v[ 4 ]  then 
                           pcall ( v[ 3 ], table.unpack (v[ 4 ]))
                   else 
                     pcall ( v[ 3 ])   -- функция без параметров --- 
                   end 
                end 
             end 
               end 
          ------------------------------------------------------- 
             sleep ( 2 )      
        end 
     end  
 --------------------------------------- 
     local   function  Start_Timer ()
     Start_thread (Timer)
     end 
   ------ 
   return  {Set_Timer  =  Set_Timer, delete_Timer  =  delete_Timer, Start_Timer  =  Start_Timer }
  

---------------------------------------------

Шаблон использования модуля:
Код
   -- Шаблон использования модуля таймерных событий 
 local  OnTi mer  =   require ( 'OnTimer' )   -- Подключение модуля обработки таймерных событий  --- 
 local  Set_Timer  =  OnTimer.Set_Timer
 local  delete_Timer  =  OnTimer.delete_Timer
 local  Start_Timer  =  OnTimer.Start_Timer

IsRun  =   true 

     local   function  call_Timer  (kl)
         message  (  'call_Timer = '   ..  kl)
     end  
   
     local   function  call_Timer1  ()
         message  (  'call_Timer1  () ==============' )
     end  

 function   main ()
    Start_Timer()
    local  kl
     --- Заказ таймерных событий ---- 
     --- Таймерное событие:  {<Имя события>, <Интервал в млсек.>, <Функция обработки события>,{<Список парвметров функции>}} 
   kl  =  Set_Timer({'Set_Timer',  100 , call_Timer, { 100 }})
   kl  =  Set_Timer({'Set_Timer',  1000 , call_Timer, { 1000 }})
   kl  =  Set_Timer({'Set_Timer',  5000 , call_Timer, { 5000 }})
   kl  =  Set_Timer({'Set_Timer',  3000 , call_Timer1})
 --   delete_Timer (kl) 
   
     ------------------- 
     while  IsRun  do  
   
        ----  Обработка в main остального ----- 
       kl  =  Set_Timer({'Set_Timer',  1000 , call_Timer, { 1000 }})
        delete_Timer (kl)
        ----  Конец Обработка в main остального ----- 
         sleep ( 500 )        
   end 
   ------------------ 
 end    
   
 function   OnStop ()
     IsRun  =   false 
     DestroyTable (TableQUIK)
      DestroyTable (TableQUIK1)
     return   10000     --  !!  По истечении этого интервала времени (в млсек.), данного скрипту на завершение работы, функция main() завершается принудительно.  
                                 -- При этом возможна потеря системных ресурсов.   ! Если функция не выдает значение, то по умолчанию оно 5000 (5 сек.) 
 end 
  
Попробуйте использовать таймеры ожидания (таймеры ядра)
Такое решение и проще и лучше. Не надо создавать новые потоки.
Квант таймера 0.1  мкс.
Программирование гораздо проще, чем ваша скатерть-самобранка.
 
Вы может сами добавить сколько хотите функций обратного вызова.
 
1.
Цитата
nikolz написал:
Попробуйте использовать таймеры ожидания (таймеры ядра)Такое решение и проще и лучше. Не надо создавать новые потоки.Квант таймера 0.1  мкс.Программирование гораздо проще, чем ваша скатерть-самобранка.
 Ну. про это я, наверное, знаю.
А вы попробуйте привести здесь свой работающий код и тогда поговорим.

2.  Тот кто соображает, поймет, что выложенный мною модуль обеспечивает возможность запускать пользователю свои функции в отдельном (от основного потока QUIK) потоке.
 
3.
Цитата
nikolz написал:
Вы может сами добавить сколько хотите функций обратного вызова.
 Вы что, не поняли и то, что в приведенном мною примере демонстрируется возможность создания таймерных событий столько, сколько потребуется?
 
Цитата
TGB написал:
3.
Цитата
nikolz написал:
Вы может сами добавить сколько хотите функций обратного вызова.
  Вы что, не поняли и то, что в приведенном мною примере демонстрируется возможность создания таймерных событий столько, сколько потребуется?
понял, но пример у вас очень громоздкий.
-----------------------------------------
все решается гораздо проще на таймерах ожидания ядра либо событиях.
При этом реакция ОС составляет не более 1 мкс.
===============
Оотносительно колбеков и одновременно таймера.
---------------------------
Реализовал  это, создав пул событий на основе  WaitForMultipleObjects.
---------------------------------
В итоге бесконечный цикл  запускается через заданный в функции ожидания интервал,
т е это тайме собаки,
либо при срабатывании любого колбека QLUA
или любой функции пользователя, для которой описано событие.
-----------------------------------
При возникновении события запускается соответствующий ему колбек.
----------------------
Количество событий- любое
Количество колбеков- любое.
Все события  контролируются OC.
=================
Примерно так.
 
Цитата
nikolz написал:
Реализовал  это, создав пул событий на основе  WaitForMultipleObjects.
В итоге бесконечный цикл  запускается через заданный в функции ожидания интервал,т е это тайме собаки,либо при срабатывании любого колбека QLUA или любой функции пользователя, для которой описано событие.
При возникновении события запускается соответствующий ему колбек.
Количество событий- любое. Количество колбеков- любое. Все события  контролируются OC.
Примерно так.
И это весь ваш (очень простой  :smile: ) работающий код, который я могу запустить на своем ПК с тем, чтобы проверить, как он замечательно работает?
--
Цитата
nikolz написал:
Все события  контролируются OC.
 Ну, если бы приведенный вами «код» работал бы, то это было бы, конечно, большое достижение, особенно с учетом того, что все исполняемые приложения контролируются ОС  :smile: .
 
1. nikolz зачем при ссылке на мой код процитировали его весь? Он все-таки длинный и достаточно было бы привести несколько строк. У вас что, есть план по объему генерации текстов своих комментариев  :smile: ?
2. Это
Цитата
nikolz написал:
Квант таймера 0.1  мкс.
вы написали не случайно  :smile: ? А если так, то можно предположить, что это разрешение вашего гипотетического таймера, который вы собираетесь создать (готового пока у вас я не вижу ничего) используя
Цитата
nikolz написал:
пул событий на основе  WaitForMultipleObjects.
Разрешение таймера в 0.1 мкс в Windows (только не путайте с возможностью засечки времени в Windows с использованием QueryPerformanceCounter) было бы круто не только для QLua, но и для C. Я, правда, не очень понимаю, зачем такое разрешение нужно в QLua, но ваша заява так впечатляет и претендует на открытие, что мне стало интересно, а вдруг вы Кулибин от программирования  :smile: .
   Вам известно, что интервалы в WaitForMultipleObjects можно задать только в млсек.?  Вообще, расскажите подробно в деталях, что вам известно о WaitForMultipleObjects (возможно, неизвестное мне), позволяющее, по-вашему, мнению, использовать эту функцию для обеспечения разрешения вашего гипотетического таймера в 0.1 мкс. Только не рассказывайте, это на «растопыренных»  пальцах, а приведите нормальное API и к нему нормальный код (лучше, ваш, работающий, или хотя бы, найденный в сети) на каком-нибудь, известном вам языке программирования. Я попытаюсь такой код понять и как то его прокомментировать.
Страницы: 1
Читают тему (гостей: 1)
Наверх