1) Добавить функцию обратного вызова OnTimer(), которая будет вызываться терминалом с некоторой периодичностью. Надо понять, как указывать период срабатывания. Конкретно для моих задач хватит срабатывания раз в секунду, но мало ли кому чаще надо. Предлагаю обсудить, кому интересно.
Пускай первый раз функция OnTimer() запускается сразу после OnInit() и возвращает число число миллисекунд, через которое будет сделан следующий вызов функции OnTimer(). Если возвращается не число, то пусть следующий вызов будет через 1 секунду.
Реализация на стороне терминала кажется несложной.
2) Дать пользователю возможность из main-потока подать команду на запуск своей функции в потоке коллбэков. Нормальное название не придумал, но надо что-то типа
Код
executeAsCallback(function() ... end)
В принципе, имея п.2, пользователь сможет самостоятельно реализовать п.2, если только в main-потоке не выполняются тяжёлые вычислительные задачи.
Надо всё это для того, чтобы можно было выполнять в потоке коллбэков регулярные задачи, которые удобнее и архитектурно правильнее делать там, а не в 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.
_sk_ написал: Надо всё это для того, чтобы можно было выполнять в потоке коллбэков регулярные задачи, которые удобнее и архитектурно правильнее делать там, а не в main.
Архитектурно правильно (в тех рамках, что мы имеем) как можно меньше нагружать колбеки основного потока, вынося всё что возможно в main(). Некоторые еще и несколько потоков делают, этакие множественные main(). Или даже в отдельный процесс. А потому всё же хотелось бы услышать что это за такие "архитектурно правильные задачи", которые нельзя вынести в main().
Есть список слушателей, которых надо уведомлять о коллбэках (каждого слушателя о своём подмножестве коллбэков) как можно быстрее. При этом в main() время от времени возникают вычислительно тяжёлые задачи (заранее нельзя контролировать время их выполнения и разбивать на мелкие подзадачи).
Если пробрасывать события из потока коллбэков в поток main(), то будут возникать задержки, чего не стоит допускать.
Есть потребность в отключении слушателей по таймауту (а не когда придёт очередной коллбэк) и подключении новых вне зависимости от приходящих коллбэков. При этом итерирование по таблице с добавлением/удалением сразу в двух потоках без должной синхронизации как-то плохо выглядит.
Выходит, что удобно иметь возможность инициировать какой-то коллбэк из потока main, не надо думать про синхронизацию. Пункты 1 и 2 были обсуждением таких возможностей.
_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(), естественно, вызываем что-то свое полезное.
_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 секунд, включаем анализ
...
КТО ИМЕННО "полностью прекращает свою работу и просто ждет это время"? Скрипт? Так это просто интерпретируемый текст - он вообще никогда не работает.
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.
Скрытый текст
Код
local run = true
local tid
function OnStop()
run = nil
local t = tid
tid = nil
if t then DestroyTable(t) end
end
local result
local function RunAsCallback()
SetSelectedRow(tid, 2)
local r = result
result = nil
return r
end
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)
if QTABLE_SELCHANGED == m then
if w == 2 then
SetSelectedRow(tid, 1)
result = os.date()
end
end
end)
while run do
local date = RunAsCallback()
message(date)
sleep(1000)
end
OnStop()
end
Хотелось бы что-то типа такого "из коробки" без необходимости дёргать GUI.
Надо делать так, как надо. А как не надо - делать не надо.
Ну если уж просить, почему бы не попросить еще один поток для своих колбеков. Т.е. реализовать методы регистрации своих функций обратного вызова и отдельный поток для них, чтобы не лезть в основной поток вовсе.
Nikolay написал: Ну если уж просить, почему бы не попросить еще один поток для своих колбеков. Т.е. реализовать методы регистрации своих функций обратного вызова и отдельный поток для них, чтобы не лезть в основной поток вовсе.
То есть, это то что, реализовано в OS_Quesha. У меня есть гипотеза, почему это, до сих пор. не доступно для многих пользователей QUIK, но объявлять это не корректно.
Старатель написал: Отдельный поток у вас уже есть - main. Зачем ещё один городить?
Дело в том, что вы так или иначе всё равно лезете в основной поток: GUI, хранилище данных.
Да, но зачем нагружать поток терминала, пусть себе в сторонке что-то делается. Тогда можно было бы и нагруженные модули писать. Сейчас же в основном потоке только данные заполнить в общей области видимости, никаких ожиданий и долгих вычислений.
Nikolay написал: Да, но зачем нагружать поток терминала, пусть себе в сторонке что-то делается. Тогда можно было бы и нагруженные модули писать. Сейчас же в основном потоке только данные заполнить в общей области видимости, никаких ожиданий и долгих вычислений.
Справедливое замечание. Конечно, между колбеками и функцией main, следует создавать очереди, которые потокобезопасны, но кто это понимает? Большинство из тех, кто заходит на этот сайт, не проффесоналы.
Кто сказал "нагружать"? Задачи на 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 написал: если у пользователя запущено несколько скриптов, то пока основной поток 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.
_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 мкс. Программирование гораздо проще, чем ваша скатерть-самобранка.
nikolz написал: Попробуйте использовать таймеры ожидания (таймеры ядра)Такое решение и проще и лучше. Не надо создавать новые потоки.Квант таймера 0.1 мкс.Программирование гораздо проще, чем ваша скатерть-самобранка.
Ну. про это я, наверное, знаю. А вы попробуйте привести здесь свой работающий код и тогда поговорим.
2. Тот кто соображает, поймет, что выложенный мною модуль обеспечивает возможность запускать пользователю свои функции в отдельном (от основного потока QUIK) потоке.
nikolz написал: Вы может сами добавить сколько хотите функций обратного вызова.
Вы что, не поняли и то, что в приведенном мною примере демонстрируется возможность создания таймерных событий столько, сколько потребуется?
понял, но пример у вас очень громоздкий. ----------------------------------------- все решается гораздо проще на таймерах ожидания ядра либо событиях. При этом реакция ОС составляет не более 1 мкс. =============== Оотносительно колбеков и одновременно таймера. --------------------------- Реализовал это, создав пул событий на основе WaitForMultipleObjects. --------------------------------- В итоге бесконечный цикл запускается через заданный в функции ожидания интервал, т е это тайме собаки, либо при срабатывании любого колбека QLUA или любой функции пользователя, для которой описано событие. ----------------------------------- При возникновении события запускается соответствующий ему колбек. ---------------------- Количество событий- любое Количество колбеков- любое. Все события контролируются OC. ================= Примерно так.
nikolz написал: Реализовал это, создав пул событий на основе WaitForMultipleObjects. В итоге бесконечный цикл запускается через заданный в функции ожидания интервал,т е это тайме собаки,либо при срабатывании любого колбека QLUA или любой функции пользователя, для которой описано событие. При возникновении события запускается соответствующий ему колбек. Количество событий- любое. Количество колбеков- любое. Все события контролируются OC. Примерно так.
И это весь ваш (очень простой ) работающий код, который я могу запустить на своем ПК с тем, чтобы проверить, как он замечательно работает? --
Ну, если бы приведенный вами «код» работал бы, то это было бы, конечно, большое достижение, особенно с учетом того, что все исполняемые приложения контролируются ОС .
1. nikolz зачем при ссылке на мой код процитировали его весь? Он все-таки длинный и достаточно было бы привести несколько строк. У вас что, есть план по объему генерации текстов своих комментариев ? 2. Это
вы написали не случайно ? А если так, то можно предположить, что это разрешение вашего гипотетического таймера, который вы собираетесь создать (готового пока у вас я не вижу ничего) используя
Цитата
nikolz написал: пул событий на основе WaitForMultipleObjects.
Разрешение таймера в 0.1 мкс в Windows (только не путайте с возможностью засечки времени в Windows с использованием QueryPerformanceCounter) было бы круто не только для QLua, но и для C. Я, правда, не очень понимаю, зачем такое разрешение нужно в QLua, но ваша заява так впечатляет и претендует на открытие, что мне стало интересно, а вдруг вы Кулибин от программирования . Вам известно, что интервалы в WaitForMultipleObjects можно задать только в млсек.? Вообще, расскажите подробно в деталях, что вам известно о WaitForMultipleObjects (возможно, неизвестное мне), позволяющее, по-вашему, мнению, использовать эту функцию для обеспечения разрешения вашего гипотетического таймера в 0.1 мкс. Только не рассказывайте, это на «растопыренных» пальцах, а приведите нормальное API и к нему нормальный код (лучше, ваш, работающий, или хотя бы, найденный в сети) на каком-нибудь, известном вам языке программирования. Я попытаюсь такой код понять и как то его прокомментировать.