Старатель написал: Кто сказал "нагружать"? Задачи на 0.1-2 мс, без фанатизма.Я даже не понимаю тех, кто в стремлении "разгрузить" основной поток перекидывает простейшие задачи (с временем выполнения менее 2 мс) из колбеков в main (что опять же не бесплатно в плане нагрузки), создавая километровые очереди и забывая об общей производительности.
С написанным можно бы согласиться, но с учетом следующих моментов: 1) байт-коды основного потока QUIK и пользовательского потока main выполняются последовательно и пока выполняется один, другой ждет; то есть использование основного потока для выполнения функций пользователя параллелизм добавляет только при выполнении C-функций; 2) если у пользователя запущено несколько скриптов, то пока основной поток QUIK выполняется в одном из скриптов, он перестает обслуживать другие скрипты пользователя (в том числе, и созданные им таблицы QUIK); 3) вами выложены в ветке «Кривые ошибки в QLUA» очень эффективные потокобезопасные функции работы с очередями без синхронизации между основным потоком QUIK и потоком main; 4) перенос коротких задач пользователя в колбеки делает скрипт пользователя многопоточным, то есть, при этом пользователю нужно следить за тем, нет ли в его скрипте проблем синхронизации.
Nikolay написал: Да, но зачем нагружать поток терминала, пусть себе в сторонке что-то делается. Тогда можно было бы и нагруженные модули писать. Сейчас же в основном потоке только данные заполнить в общей области видимости, никаких ожиданий и долгих вычислений.
Справедливое замечание. Конечно, между колбеками и функцией main, следует создавать очереди, которые потокобезопасны, но кто это понимает? Большинство из тех, кто заходит на этот сайт, не проффесоналы.
Nikolay написал: Ну если уж просить, почему бы не попросить еще один поток для своих колбеков. Т.е. реализовать методы регистрации своих функций обратного вызова и отдельный поток для них, чтобы не лезть в основной поток вовсе.
То есть, это то что, реализовано в OS_Quesha. У меня есть гипотеза, почему это, до сих пор. не доступно для многих пользователей QUIK, но объявлять это не корректно.
Nikolay написал: Если простыми словами - то это функция объект с своей областью видимости. Что дает возможность создавать разные экземпляры функции с своими переменными.
А если совсем по-простому, то это модуль, сохраняющий свое состояние (историю своего запуска) между своими запусками на исполнение.
Средства разработки многопоточных скриптов в QUIK., OS_Quesha, свидетельство регистрации в Роспатенте № RU 2020612905. Бесплатная для некоммерческого использования.
Средства отладки OS_Quesha. В OS_Quesha средства отладки представлены на трех уровнях: 1) Отладка кода; 2) Отладка приложения на уровне взаимодействия их потоков; 3) Отладка стратегий торговли на фондовом рынке. Для отладки кода обеспечены следующие возможности: 1) Ведется потокобезопасный журнал отладки, представляющий собой циклический буфер, реализованный с использованием двух текстовых файлов, между которыми выполняется переключение при заполнении текущего файла. Этот буфер обеспечивает корректную запись сообщений из различных потоков OS_Quesha с высокой частотой. Размер этого буфера задается в глобальных настройках переменной Size_log в кб. События в журнал записываются с точностью до 1 миллисекунды; 2) Печать в скрипте функцией dump_str переменных любых типов, в том числе таблиц QLua со всем их содержимым произвольной вложенности (с существующими метатаблицами); 3) Онлайн печать (при реальной работе) из меню любых глобальных переменных вместе с их содержимым; 4) Использование в скрипте Pcall_dll, обеспечивающей выполнение функций в защищенном режиме, с централизованной выдачей стека вызова функции в случае возникновения исключений. Дополнительно, если это требуется, многопоточное приложение, в котором возникло такое исключение, останавливается для после-дующего анализа возникшей ошибки; 5) Использование функции paramete_control для контроля типов параметров, вызываемых функций; 6) обработка высокочастотных сообщений об ошибках. Для отладки приложения на уровне взаимодействия их потоков можно использовать: 1) Настраиваемый, мало влияющий на функционирование приложения, фильтрую-щий вывод сообщений очередей взаимодействия потоков приложения, с привязкой их ко времени, показывающему динамическую картину, в этих очередях; 2) Команду запуска скриптов из меню отладки, обеспечивающую отладку фрагментов разрабатываемых программ в среде OS_Quesha, в процессе продуктивного функционирования приложения; 3) Команды Останов / Пуск, обеспечивающие «замораживание»/«размораживание» потоков приложения для возможности анализа их состояния. Отладка стратегий торговли на фондовом рынке обеспечивается: 1) Интерфейсом прогона стратегий торговли на внутренних данных QUIK и истории ко-тировок бумаг, хранимых в базах SQLite; 2) Оперативно сохраняемой историей одноминуток и пятиминуток конкретных, заданных в настройках бумаг, хранимых в базах SQLite; 3) Оперативно обновляемой историей данных по 18 параметрам (конкретных, заданных в настройках бумаг), получаемых по событиям OnParam, с задержкой (относительно этих событий) < 1 млсек. и записываемых в вектора в оперативной памяти; 4) Реализацией виртуальных операций на реальном рынке.
Где реакция поддержки? ---- Третий раз (первый комментарий был написан 10.09.2021 07:46:28 https://forum.quik.ru/messages/forum10/message57872/topic5823/#message57872) обращаю внимание поддержки на то, что тест, выложенный 10.09.2021 07:46:28 в данной ветке, в QUIK (8.13.1.16, 9.1.3.11 и 9.2.1.4 (с последними обновлениями), в QLua 5.3 демонстрирует ошибку в запусках колбеков. Это подтверждено пользователем Anton (смотрите комментарий 61). Если перевести на простой язык, то диагностируется ошибка синхронизации выполнения потока запуска колбеков и потока main скрипта (и это после полутора года эксплуатации новых версий QUIK ). Ошибки синхронизации могут проявляться как случайные сбои в сриптах пользователей в различные моменты времени. Что из выше написанного не понятно? А если непонятно, то где вопросы?
Просьба (повторная) к поддержке: довести содержимое моего комментария: https://forum.quik.ru/messages/forum10/message57872/topic5823/#message57872 до разработчиков QUIK. В QUIK 9.2.1.4 (с последними обновлениями) тестовый скрипт, выложенный в упомянутом выше комментарии, демонстрирует, по моему мнению, ошибку в запусках колбеков, в QLua 5.3 ( в QLua 5.4 эта ошибка не диагностируется, но это не означает, что в 5.4 такой ошибки нет).
Anton написал: TGB , у меня для вас две новости, как водится: 1) в 5.3 воспроизводится буквально сразу, запущено 4 скрипта и заказаны все параметры по всем инструментам (на боевом); 2) в 5.4 в тех же условинях НЕ ВОСПРОИЗВОДИТСЯ.
Действительно, в Qlua 5.4 тест ошибку не диагностирует. --- Спасибо Антону за его уточнение. -- А где поддержка?
Anton написал: Все такое же, имена поменяйте. Там может вообще темплейт в сорце.
Имена я способен поменять, но мы же обсуждаем не то что я могу сделать, а как это сделано в текущем QLua.
Цитата
Anton написал: Это значит, что коллектор начинает агрессивничать и влезать в любую доступную дыру. Где он там дыру в мейне нашел, науке пока неизвестно.
В принципе, если обеспечено четкое разделение между потоками, среды исполнения Lua (то есть, она разделяемый ресурс с корректной синхронизацией доступа к ней) то это эквивалентно однопоточному Lua, в котором ошибки, конечно есть, но их мало.
Что то, не сильно, похоже на детали. Например, не охватывается случай "OnParam". А как вы обясните:
Цитата
Anton написал: в 5.3 воспроизводится буквально сразу, запущено 4 скрипта и заказаны все параметры по всем инструментам (на боевом
----
Цитата
Anton написал: Это lua_lock() + lua_getglobal() + lua_unlock(), совершенно бессмысленно это проделывать, точно зная, что в скрипте нет такой функции. Дискутировать лень.
Ну, конечно, вместо lua_getglobal(...) (кстати, lua_lock() и lua_unlock() выполняется внутри функции lua_getglobal), эквивалентной с точки зрения времени обращения, обращению к глобальной переменной, даже если этой переменной не существует, можно заняться более содержательным алгоритмом регистрации колбеков скриптов (где можно и ошибиться - замечательная возможность).
Anton написал: в 5.3 воспроизводится буквально сразу, запущено 4 скрипта и заказаны все параметры по всем инструментам (на боевом);
Все-таки, воспроизводится. ----
Цитата
Anton написал: в 5.4 в тех же условинях НЕ ВОСПРОИЗВОДИТСЯ.
Ну. тогда АРКИ надо объявить, что в версия QLua 5.3 не работоспособна. ---
Цитата
Anton написал: колбеки так и запускаются, плюс-минус детали.
Раскажите детали. Мне это было бы интересно.
Цитата
Anton написал: Если не был объявлен, квик его не будет дергать. Очень даже хорошая оптимизация, иначе квик бы на каждое событие разыскивал необъявленные функции во всех скриптах, что несколько затратно, учитывая локи.
Вы, наверное, знаете, что обращение lua_getglobal (<Основной State в котором запускается колбек>, <Имя колбека>) это эквивалентно обращение в тексте скрипта к переменной скрипта (тоесть. выполняется быстро). Это же обращение к полю таблицы. Или вы думаете, что это не так? Тогда подискутируем. А если это так, то о каком "разыскивании по всем скриптам" идет речь, когда происходит обращение к глобальной таблице, определяемой <Основной State в котором запускается колбек>. Вообще то, мне было бы интересно пообщаться с главным разработчиком QUIK. Но вы же, похоже не главный?
Поддержка понимает, что пока не будут устранены "кривые, мерцающие, редкие" ошибки QLua, на нее будет постоянно валиться вал разнообразных ошибок из различных функциональностей QLua? Если бы я был разработчиком QLua, то в первую очередь занимался бы устранением именно "кривых" ошибок. Это глобальные ошибки, которые могут проявляться у пользователя в любых местах его скриптов, вводя в заблуждения относительно источника ошибок.
Я не очень понимаю в чем могут быть проблемы запуска колбеков QLua. Вот вариант как это можно сделать просто ( C-API Lua):
Код
if ( lua_getglobal (<Основной State в котором запускается колбек>, <Имя колбека>) == LUA_TFUNCTION) // запись и проверка переменной колбека скрипта в стек
{
if ( lua_pcall(<Основной State в котором запускается колбек>, <Количество параметров колбека>, <Список параметров колбека>) ~= 0 ) // запуск колбека
{
<Обработка ошибки при запуске колбека>
}
}
else // в переменной колбека не функция
{
<Обработка ошибки пользователя, задавшего в качестве колбека не функцию>
}
Этот вариант обеспечивает возможность (отсутствующую в существующей версии QLua) замены пользователем функции, вызываемой в качестве колбека, при исполнении скрипта.
Просьба к поддержке довести содержимое этого комментария до разработчиков QUIK. ----- В версиях QUIK (8.13.1.16 и 9.1.3.11), похоже, есть ошибка, демонстрируемая кодом скрипта, который выложен в комментарии далее. Этот скрипт появился в результате анализа редко возникающей «мерцающей» ошибки (похожей на ошибки QLua, описанные Старателем в данной ветке), когда в переменной сразу после присвоения ей значения, это значение на следующем шаге оказывалось искаженным. При одновременном запуске трех экземпляров скриптов в QUIKе, подключенного к учебному серверу, в разные моменты времени, но достаточно часто (~ c 10 минутным ин-тервалом) выдается сообщение о том, что параллельно (одновременно) выполняются байт-код потока колбека и байт-код потока main (смотрите текст скрипта). В существующей реализации QLua такого быть не должно. Можно предположить, что в QLua есть ошибка в реализации запуска колбеков. --- Код скрипта:
Код
--- Функции работы с очередями (написаны на "чистом" Lua без обращения к C-функциям) ---
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 size = function (self)
return self.last - self.first + 1
end
-----
--------- Создание очереди------
local Queue_QUIK = new()
---------------------------------------
--- Основной поток обработки колбеков "обходит", запушенные на выполнение скрипты, с тем чтобы по событиям запускать их колбеки.
-- При этом должна быть обеспечена корректная синхронизация выполнения байт-кода колбека таким образом, что он не может выполняеться
-- параллельно с байт-кодом потока main.
----------------------
local for_OnParam = 2 -- Множитель количества записей в очередь ----
local N_OnParam = 0
--- OnParam написана на "чистом" Lua без обращения к C-функциям ---
function OnParam(p1, p2)
for i = 1, for_OnParam do
N_OnParam = N_OnParam + 1
-- Зпаись события ---
push (Queue_QUIK, {'OnParam', N_OnParam, {p1, p2}}) -- 1. здесь создаются таблицы и может быть запущена автоматическая сборка мусора,
-- которая работает для всей инсталяции Lua и не является потокобезопасной ---
end
end
---- Обработка событий QUIK (написана на "чистом" Lua без обращения к C-функциям) ----
local function handle_events()
----- #### Для отладки ---
local OnParam_begin = N_OnParam
local size_Queue_QUIK = size(Queue_QUIK)
---------------------------------
--- Чтение очереди событий -----
local ms, first_q = pop (Queue_QUIK)
while ( ms ) do --- Есть события ---
-------------------------------
--- Чтение очереди событий -----
ms, first_q = pop (Queue_QUIK) --Queue_QUIK: pop()
end
---
for i = 1, 1000 do end ---- нагрузка #### Отладка ---
if N_OnParam ~= OnParam_begin then -- Счетчик N_OnParam может иpмениться только в OnParam
-- А это означает, что одновременно (параллельно) работают поток main и основной
-- поток обработки колбеков, что недопустимо.
--- это C-функция, но она вызывается только если N_OnParam ~= OnParam_begin
message ( ' Параллельное выполнение колбека и потока main: N_OnParam - OnParam_begin = ' .. tostring(N_OnParam - OnParam_begin)
.. ' size_Queue_QUIK = ' .. tostring(size_Queue_QUIK) )
end
----
end
--------------------------------------------------------------------------------------------------------------------
--------
IsRun = true
function main()
-------------------
while IsRun do
handle_events() ---- функция чтения событий OnParam ---
sleep(2)
end
------------------
end
function OnStop()
IsRun = false
return 10000
end
Средства разработки многопоточных скриптов в QUIK., OS_Quesha, свидетельство регистрации в Роспатенте № RU 2020612905. Бесплатная для некоммерческого использования.
В моем комментарии https://forum.quik.ru/messages/forum10/message57220/topic5823/#message57220 в п.2 было описано предложение по добавлению интерфейса работы с событиями QUIK. Ниже выложен код модуля реализации этого предложения на «чистом» Qlua, а также шаблон использования этого модуля. В этом шаблоне демонстрируется схема обработки событий фондового рынка и событий таблиц QUIK. Для подключения модуля к шаблону его надо сохранить в файле под именем even_handling_module.lua в папку хранения кода запуска QUIK (info.exe). Код модуля и шаблона можно использовать свободно. При его распространении желательна ссылка на меня. Описание использования модуля приведено в его коде и коде шаблона, описывающего схему обработки событий QUIK. ---- Код модуля:
Код
--[[ TGB ---- Модуль реализации обработки событий QUIK с использованием очередей событий фондового рынка и событий таблиц QUIK ---
Краткая спецификация модуля.
1. Реализуется схема взаимодействия main с колбеками через две потокобезопасные эффективные очереди, одна из которых обслуживает колбеки
фондового рынка, а вторая, колбеки событий таблиц QUIK, созданных скриптером;
2. При работе обработке колбеков есть две функции: <функция подписки на события фондового рынка>
и <функция подписки на события пользовательских таблиц >; в этих функциях скриптер может, в виде списка (в таблице) подписаться на события
в соответствии с существующими именами колбеков; реализация подписки сведена к автоматическому созданию (скрытых) от пользователя
функций колбеков, записывающих свои параметры с добавлением «от кого параметры» в соответствующие очереди;
3. Для работы с очередями по новой схеме есть две функции чтения соответственно очередям;
при чтении очередей в считываемой таблице передаются: <имя события>, <время записи в очередь>, <параметры в том виде, как они описаны, в существующих колбеках>.
При работе по этой схеме, QLua для скриптера становится однопоточным и снимаются многие сложные проблемы.
----
!! Колбеки OnInit и OnStop обрабатываются как обычно.
-------------------------
--]]
---- Функции работы с очередями (выложенные на форуме Старателем) ------
--- При использовании между потоком колбеков и потоком main потокобезопасные
local Queue = {
new = function (self)
return setmetatable({first = 1 , last = 0 }, {__index = self})
end ,
push = function (self, v)
local last = self.last + 1
self[last] = v
self.last = last
return last
end ,
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
end ,
size = function (self)
return self.last - self.first + 1
end
}
--------- Создание очередей-----------
local Queue_QUIK = Queue:new()
local Queue_QUIK_tbl = Queue:new()
----- Функции чтения очередей -------
local function Queue_evn_QUIK() return Queue_QUIK: pop() end
local function Queue_evn_QUIK_tbl() return Queue_QUIK_tbl: pop() end
----- Функции запроса текущих размеров очередей -------
local function sizeQueue_evn_QUIK() return Queue_QUIK: size() end
local function sizeQueue_evn_QUIK_tbl() return Queue_QUIK_tbl: size() end
-- Конец Функции работы с очередями ---------------------------------------
local function f_nil () end
--- Переменные событий фондового рынка -------
OnAccountBala nce = f_nil --- изменение позиции по счету
OnAccountPosit ion = f_nil --- изменение позиции по счету
OnAllTr ade = f_nil --- новая обезличенная сделка
OnClea nUp = f_nil --- смена торговой сессии
OnCl ose = f_nil --- закрытие терминала QUIK или выгрузка файла qlua.dll
OnConnec ted = f_nil --- установление связи с сервером QUIK
OnDepoLi mit = f_nil --- изменение бумажного лимита
OnDepoLimitDel ete = f_nil --- удаление бумажного лимита
OnDisconnec ted = f_nil --- отключение от сервера QUIK
OnF irm = f_nil --- получение описания новой фирмы
OnFuturesClientHold ing = f_nil --- изменение позиции по срочному рынку
OnFuturesLimitCha nge = f_nil --- изменение ограничений по срочному рынку
OnFuturesLimitDel ete = f_nil --- удаление лимита по срочному рынку
-- OnI nit = f_nil --- инициализация функции main
OnMoneyLi mit = f_nil --- изменение денежного лимита
OnMoneyLimitDel ete = f_nil --- удаление денежного лимита
OnNegD eal = f_nil --- новая заявка на внебиржевую сделку или изменение параметров существующей заявки на внебиржевую сделку
OnNegTr ade = f_nil --- новая сделка для исполнения или изменение существующей сделки для исполнения
OnOr der = f_nil --- новая заявка или изменение параметров существующей заявки
OnPa ram = f_nil --- изменение текущих параметров
OnQu ote = f_nil --- изменение стакана котировок
-- OnS top = f_nil --- остановка скрипта из диалога управления
OnStopOr der = f_nil --- новая стоп-заявка или изменение параметров существующей стоп-заявки
OnTr ade = f_nil --- новая сделка или изменение параметров существующей сделки
OnTransRe ply = f_nil --- ответ на транзакцию
------
local unsubscribe_tbl = ---- Функции отписки ----
{
['OnAccountBalance'] = function () OnAccountBala nce = f_nil end
, ['OnAccountPosition'] = function () OnAccountPosit ion = f_nil end
, ['OnAllTrade'] = function () OnAllTr ade = f_nil end
, ['OnCleanUp'] = function () OnClea nUp = f_nil end
, ['OnClose'] = function () OnCl ose = f_nil end
, ['OnConnected'] = function () OnConnec ted = f_nil end
, ['OnDepoLimit'] = function () OnDepoLi mit = f_nil end
, ['OnDepoLimitDelete'] = function () OnDepoLimitDel ete = f_nil end
, ['OnDisconnected'] = function () OnDisconnec ted = f_nil end
, ['OnFirm'] = function () OnF irm = f_nil end
, ['OnFuturesClientHolding'] = function () OnFuturesClientHold ing = f_nil end
, ['OnFuturesLimitChange'] = function () OnFuturesLimitCha nge = f_nil end
, ['OnFuturesLimitDelete'] = function () OnFuturesLimitDel ete = f_nil end
-- , ['OnInit'] = function () OnI nit = f_nil end
, ['OnMoneyLimit'] = function () OnMoneyLi mit = f_nil end
, ['OnMoneyLimitDelete'] = function () OnMoneyLimitDel ete = f_nil end
, ['OnNegDeal'] = function () OnNegD eal = f_nil end
, ['OnNegTrade'] = function () OnNegTr ade = f_nil end
, ['OnOrder'] = function () OnOr der = f_nil end
, ['OnParam'] = function () OnPa ram = f_nil end
, ['OnQuote'] = function () OnQu ote = f_nil end
-- , ['OnStop'] = function () OnS top = f_nil end
, ['OnStopOrder'] = function () OnStopOr der = f_nil end
, ['OnTrade'] = function () OnTr ade = f_nil end
, ['OnTransReply'] = function () OnTransRe ply = f_nil end
}
-----
local subscribe_tbl = ---- Функции подписки ----
{
['OnAccountBalance'] = function () OnAccountBala nce = function (...) Queue_QUIK: push ({'OnAccountBalance', os.time (), {...}} ) end end
, ['OnAccountPosition'] = function () OnAccountPosit ion = function (...) Queue_QUIK: push ({'OnAccountPosition', os.time (), {...}} ) end end
, ['OnAllTrade'] = function () OnAllTr ade = function (...) Queue_QUIK: push ({'OnAllTrade', os.time (), {...}} ) end end
, ['OnCleanUp'] = function () OnClea nUp = function (...) Queue_QUIK: push ({'OnCleanUp', os.time (), {...}} ) end end
, ['OnClose'] = function () OnCl ose = function (...) Queue_QUIK: push ({'OnClose', os.time (), {...}} ) end end
, ['OnConnected'] = function () OnConnec ted = function (...) Queue_QUIK: push ({'OnConnected', os.time (), {...}} ) end end
, ['OnDepoLimit'] = function () OnDepoLi mit = function (...) Queue_QUIK: push ({'OnDepoLimit', os.time (), {...}} ) end end
, ['OnDepoLimitDelete'] = function () OnDepoLimitDel ete = function (...) Queue_QUIK: push ({'OnDepoLimitDelete', os.time (), {...}} ) end end
, ['OnDisconnected'] = function () OnDisconnec ted = function (...) Queue_QUIK: push ({'OnDisconnected', os.time (), {...}} ) end end
, ['OnFirm'] = function () OnF irm = function (...) Queue_QUIK: push ({'OnFirm', os.time (), {...}} ) end end
, ['OnFuturesClientHolding'] = function () OnFuturesClientHold ing = function (...) Queue_QUIK: push ({'OnFuturesClientHolding', os.time (), {...}} ) end end
, ['OnFuturesLimitChange'] = function () OnFuturesLimitCha nge = function (...) Queue_QUIK: push ({'OnFuturesLimitChange', os.time (), {...}} ) end end
, ['OnFuturesLimitDelete'] = function () OnFuturesLimitDel ete = function (...) Queue_QUIK: push ({'OnFuturesLimitDelete', os.time (), {...}} ) end end
-- , ['OnInit'] = function () OnI nit = function (...) Queue_QUIK: push ({'OnInit', os.time (), {...}} ) end end
, ['OnMoneyLimit'] = function () OnMoneyLi mit = function (...) Queue_QUIK: push ({'OnMoneyLimit', os.time (), {...}} ) end end
, ['OnMoneyLimitDelete'] = function () OnMoneyLimitDel ete = function (...) Queue_QUIK: push ({'OnMoneyLimitDelete', os.time (), {...}} ) end end
, ['OnNegDeal'] = function () OnNegD eal = function (...) Queue_QUIK: push ({'OnNegDeal', os.time (), {...}} ) end end
, ['OnNegTrade'] = function () OnNegTr ade = function (...) Queue_QUIK: push ({'OnNegTrade', os.time (), {...}} ) end end
, ['OnOrder'] = function () OnOr der = function (...) Queue_QUIK: push ({'OnAccountBalance', os.time (), {...}} ) end end
, ['OnParam'] = function () OnPa ram = function (...) Queue_QUIK: push ({'OnParam', os.time (), {...}} ) end end
, ['OnQuote'] = function () OnQu ote = function (...) Queue_QUIK: push ({'OnQuote', os.time (), {...}} ) end end
-- , ['OnStop'] = function () OnS top = function (...) Queue_QUIK: push ({'OnStop', os.time (), {...}} ) end end
, ['OnStopOrder'] = function () OnStopOr der = function (...) Queue_QUIK: push ({'OnStopOrder', os.time (), {...}} ) end end
, ['OnTrade'] = function () OnTr ade = function (...) Queue_QUIK: push ({'OnTrade', os.time (), {...}} ) end end
, ['OnTransReply'] = function () OnTransRe ply = function (...) Queue_QUIK: push ({'OnTransReply', os.time (), {...}} ) end end
}
-----
-- Подписаться на события фондового рынка --
--- Ключи таблицы - имена событий ----
local function subscribe (tbl)
local err ={}, f
for k, v in next, tbl do
f = subscribe_tbl [k]
if f then f() else err[#err +1] = k end
end
return err
end
-- Отписаться от событий фондового рынка --
--- Ключи таблицы - имена событий ----
local function unsubscribe (tbl)
local err ={}, f
for k, v in next, tbl do
f = unsubscribe_tbl [k]
if f then f() else err[#err +1] = k end
end
return err
end
--- Вывод данных о состоянии подписки на события фондового рынка ---
local function condition_subscribe ()
local tbl= {}
---
tbl['OnAccountBalance'] = OnAccountBalance ~= f_nil or nil
tbl['OnAccountPosition'] = OnAccountPosition ~= f_nil or nil
tbl['OnAllTrade'] = OnAllTrade ~= f_nil or nil
tbl['OnCleanUp'] = OnCleanUp ~= f_nil or nil
tbl['OnClose'] = OnClose ~= f_nil or nil
tbl['OnConnected'] = OnConnected ~= f_nil or nil
tbl['OnDepoLimit'] = OnDepoLimit ~= f_nil or nil
tbl['OnDepoLimitDelete'] = OnDepoLimitDelete ~= f_nil or nil
tbl['OnDisconnected'] = OnDisconnected ~= f_nil or nil
tbl['OnFirm'] = OnFirm ~= f_nil or nil
tbl['OnFuturesClientHolding'] = OnFuturesClientHolding ~= f_nil or nil
tbl['OnFuturesLimitChange'] = OnFuturesLimitChange ~= f_nil or nil
tbl['OnFuturesLimitDelete'] =OnFuturesLimitDelete ~= f_nil or nil
-- tbl['OnInit'] = OnInit ~= f_nil or nil
tbl['OnMoneyLimit'] = OnMoneyLimit ~= f_nil or nil
tbl['OnMoneyLimitDelete'] = OnMoneyLimitDelete ~= f_nil or nil
tbl['OnNegDeal'] = OnNegDeal ~= f_nil or nil
tbl['OnNegTrade'] = OnNegTrade ~= f_nil or nil
tbl['OnOrder'] = OnOrder ~= f_nil or nil
tbl['OnParam'] = OnParam ~= f_nil or nil
tbl['OnQuote'] = OnQuote ~= f_nil or nil
-- tbl['OnStop'] = OnStop ~= f_nil or nil
tbl['OnStopOrder'] = OnStopOrder ~= f_nil or nil
tbl['OnTrade'] = OnTrade ~= f_nil or nil
tbl['OnTransReply'] = OnTransReply ~= f_nil or nil
return tbl
end
----------------------------------------------------------------
---- Обработка событий QUIK ----
----
local tbl_QUIK_fun --- Для хранения функции обработки событий таблиц QUIK ---
local tbl_fun --- Для хранения таблицы функции обработки событий фондового рынка ---
local period = 100 -- период счетчика для выдачи сообшений ---
local event_counter = 0 -- текущее значение циклов "разгребания" очередей событий ---
local event_counter_mess = 0 -- счетчик для выдачи сообщений --
local event_counter_mess = 0 -- счетчик для выдачи сообщений --
local read_latency_max = 0 -- максимальная задержка чтения очередей событий (сек.)--
------------------------------------------------------------------
local tbl_QUIK = {} ---- Общая таблица состояния обработки таблиц QUIK ---
---- Функция обаботки колбеков таблиц QUIK ----
local function callback_tbl_QUIK(...) --- (NUMBER t_id, NUMBER msg, NUMBER par1, NUMBER par2
local par ={...}
par = par[1]
if tbl_QUIK[par] then -- Запись в очередь событий таблиц QUIK. os.time () - дата_время записи ---
Queue_QUIK_tbl: push ({par, os.time (), {...}} )
end
end
-- Подписка на события таблицы QUIK ---
local function subscribe_tbl_QUIK (t_id, f_t)
if tbl_QUIK[t_id] then return nil end
tbl_QUIK_fun = tbl_QUIK_fun or f_t
if SetTableNotificationCallback ( t_id, callback_tbl_QUIK) == 1 then
tbl_QUIK[t_id] = 1
return nil
else
message('!!! Ошибка задания колбека для таблицы: ' .. t_id)
error('!!! Ошибка задания колбека для таблицы: ' .. t_id)
return nil
end
end
-- Отписка от событий таблицы QUIK ----
local function unsubscribe_tbl_QUIK (t_id) -- Отписка от событий таблицы QUIK ---
if tbl_QUIK [t_id] then tbl_QUIK [t_id] = nil end
if next (tbl_QUIK) == nil then tbl_QUIK_fun = nil end
end
-- Состояние подписки на события таблицы QUIK ----
local function condition_subscribe_tbl_QUIK () --- Состояние подписки на события таблиц QUIK ---
return tbl_QUIK
end
-----------------------------------------------
--- Установить функции обработки событий ----
local function install_event_handling_functions(tbl_f)
tbl_fun = tbl_f
event_counter = 0
event_counter_mess = 0
event_counter_max = 0
read_latency_max = 0
--- Подписка на события фондового рынка ---
--- ! Ключи таблицы - имена событий ----
unsubscribe (unsubscribe_tbl) ---- Отписаться от всех событий
subscribe (tbl_f) --- Подписка на события всей таблицы tbl_fun (для записей с функциями обработки событий)
end
---- Функция обработки всех событий QUIK: function handle_events(lim) -----
-- Параметр lim (по умолчанию равен 1000000) определяет максимальное количестко событий, обрабатаваемых за один вызов функции --
--- Если очереди событий пусты, то функция завершается.
-- Результат (их три): 1) количество обработанных событий; 2) максимальное количество событий за один вызов функции;
-- 3) максимальная задержка (в сек.) между записью и чтением событий
local function handle_events(lim)
lim = lim or 1000000
local f_ms
--- Чтение очередей событий -----
local ms_tbl = Queue_QUIK_tbl: pop() -- ms_tbl = {t_id, os.time (), {NUMBER t_id, NUMBER msg, NUMBER par1, NUMBER par2}}
local ms = Queue_QUIK: pop() -- ms_tbl = {<Имя события>, os.time (), {<Параметры события>}}
local os_time, os_time_d
---
while (ms_tbl or ms ) do -- Есть события ---
-------------------------------
os_time = os.time ()
event_counter = event_counter + 1
event_counter_mess = event_counter_mess + 1
if event_counter_mess >= period then
event_counter_mess = 0
message ( '!! Длительный цикл обработки событий: ' .. event_counter )
end
--- Обработка событий таблиц QUIK ---
if ms_tbl then
-- message ( tostring(ms_tbl[1]))
os_time_d = os_time - ms_tbl [2]
if read_latency_max < os_time_d then read_latency_max = os_time_d end
if tbl_QUIK_fun then tbl_QUIK_fun (unpack (ms_tbl[3])) end -- Запуск функции обработки событий таблиц QUIK ---
end
----------- События фондового рынка ---
if type (tbl_fun) == 'table' then
if ms then
os_time_d = os_time - ms [2]
if read_latency_max < os_time_d then read_latency_max = os_time_d end
f_ms = tbl_fun[ms[1]]
-- message ( tostring(ms[3][2]))
if f_ms then f_ms (unpack (ms[3])) end -- Запуск функции обработки событий фондового рынка ---
end
end
if event_counter >= lim then break end
--- Чтение очередей событий -----
ms_tbl = Queue_QUIK_tbl: pop() -- ms_tbl = {t_id, os.time (), {NUMBER t_id, NUMBER msg, NUMBER par1, NUMBER par2}}
ms = Queue_QUIK: pop()
end
--- Ведение счетчиков циклов обработки событий ----
if event_counter_max < event_counter then
event_counter_max = event_counter
end
event_counter_rt = event_counter
event_counter = 0
event_counter_mess = 0
read_latency_max = 0
return event_counter_rt, event_counter_max, read_latency_max
end
-----------------------------------------------
return {
-- Queue = Queue --- Функции работы с очередями ----
-- , Queue_evn_QUIK = Queue_evn_QUIK -- Чтение параметров событий рынка ---
-- , Queue_evn_QUIK_tbl = Queue_evn_QUIK_tbl -- Чтение параметров событий таблиц QUIK ---
sizeQueue_evn_QUIK = sizeQueue_evn_QUIK -- Текущий размер очереди событий рынка ---
, sizeQueue_evn_QUIK_tbl = sizeQueue_evn_QUIK_tbl -- Текущий размер очереди событий таблиц QUIK ---
, subscribe = subscribe -- Подписаться на события фондового рынка --
, unsubscribe = unsubscribe -- Отписаться от событий фондового рынка --
, condition_subscribe = condition_subscribe -- Сотояние подписки на события фондового рынка --
, subscribe_tbl_QUIK = subscribe_tbl_QUIK -- Подписаться на события таблиц QUIK --
, unsubscribe_tbl_QUIK = unsubscribe_tbl_QUIK -- Отписаться от событий таблиц QUIK --
, condition_subscribe_tbl_QUIK = condition_subscribe_tbl_QUIK -- Сотояние подписки на таблицы QUIK --
, install_event_handling_functions = install_event_handling_functions -- Установить функции обработки событий ----
, handle_events = handle_events -- Функция обработки всех событий QUIK -----
} ---
--------------------------------------
---- Код шаблона:
Код
local even_handling_module = require('even_handling_module') -- Подключение модуля обработки событий QUIK ---
---
local subscribe = even_handling_module.subscribe
local unsubscribe = even_handling_module.unsubscribe
local condition_subscribe = even_handling_module.condition_subscribe
local subscribe_tbl_QUIK = even_handling_module.subscribe_tbl_QUIK
local unsubscribe_tbl_QUIK = even_handling_module.unsubscribe_tbl_QUIK
local condition_subscribe_tbl_QUIK = even_handling_module.condition_subscribe_tbl_QUIK
local sizeQueue_evn_QUIK = even_handling_module.sizeQueue_evn_QUIK
local sizeQueue_evn_QUIK_tbl = even_handling_module.sizeQueue_evn_QUIK_tbl
local install_event_handling_functions = even_handling_module.install_event_handling_functions
local handle_events = even_handling_module.handle_events
IsRun = true
function main()
--- Работа с событиями таблиц QUIK -----
----- Функция обработки событий таблиц QUIK -----
-- FUNCTION (NUMBER t_id, NUMBER msg, NUMBER par1, NUMBER par2) ---
-- t_id – идентификатор таблицы; msg – код сообщения; par1 и par2 – значения параметров определяются типом сообщения msg ----
local function tbl_QUIK_fun (t_id, msg, par1, par2)
message ( 'tbl_QUIK_fun (t_id: ' .. t_id)
end
-----
TableQUIK=AllocTable() -- Создание таблицы QUIK --
AddColumn(TableQUIK, 1, "Легенда", true, QTABLE_STRING_TYPE, 6)
AddColumn(TableQUIK, 2, "Год", true, QTABLE_STRING_TYPE, 6)
AddColumn(TableQUIK, 3, "Месяц", true, QTABLE_STRING_TYPE, 6)
AddColumn(TableQUIK, 4, "День", true, QTABLE_STRING_TYPE, 6)
CreateWindow(TableQUIK)
for i=1,10 do -- Цикл заполняет ячейки
InsertRow(TableQUIK,-1)
for j=1,4 do
SetCell(TableQUIK, i, j, tostring(i).."-"..tostring(j))
end
end
TableQUIK1=AllocTable() -- Создание таблицы QUIK --
AddColumn(TableQUIK1, 1, "Легенда", true, QTABLE_STRING_TYPE, 6)
AddColumn(TableQUIK1, 2, "Год", true, QTABLE_STRING_TYPE, 6)
AddColumn(TableQUIK1, 3, "Месяц", true, QTABLE_STRING_TYPE, 6)
AddColumn(TableQUIK1, 4, "День", true, QTABLE_STRING_TYPE, 6)
CreateWindow(TableQUIK1)
for i=1,10 do -- Цикл заполняет ячейки
InsertRow(TableQUIK1,-1)
for j=1,4 do
SetCell(TableQUIK1, i, j, tostring(i).."-"..tostring(j))
end
end
---
subscribe_tbl_QUIK (TableQUIK, tbl_QUIK_fun) --- Подписка на события TableQUIK
subscribe_tbl_QUIK (TableQUIK1, tbl_QUIK_fun) --- Подписка на события TableQUIK1
-- unsubscribe_tbl_QUIK (TableQUIK) --- Отдписка от событий TableQUIK1
--- Конец Работа с событиями таблиц QUIK -----
--- Работа с событиями фондового рынка ----
--- Функции обработки событий фондового рынка ---
local function F_OnParam(p1, p2)
-- message ( 'Тикер: ' .. tostring(p2))
end
--
local function F_OnAllTrade(t)
message ( 'F_OnAllTrade: ' )
end
local function F_OnConnected(t)
message ( 'F_OnConnected: ' )
end
---- и т.д ----
-------------------------------------------------------------------
------ !!! Для перехода на обработку событий в очередях, в скрипте достаточно к именам функций колбеков добавить префикс 'F_'
--- Таблица функций обработки событий фондового рынка (параметры как в соответствующих колбеках) -----
local tbl_fun =
{
['OnAccountBalance'] = F_OnAccountBalance --- Имя функции обработки события OnAccountBalance --
, ['OnAccountPosition'] = F_OnAccountPosition -- и т.д. ---
, ['OnAllTrade'] = F_OnAllTrade
, ['OnCleanUp'] = F_OnCleanUp
, ['OnClose'] = F_OnClose
, ['OnConnected'] = F_OnConnected
, ['OnDepoLimit'] = F_OnDepoLimit
, ['OnDepoLimitDelete'] = F_OnDepoLimitDelete
, ['OnDisconnected'] = F_OnDisconnected
, ['OnFirm'] = F_OnFirm
, ['OnFuturesClientHolding'] = F_OnFuturesClientHolding
, ['OnFuturesLimitChange'] = F_OnFuturesLimitChange
, ['OnFuturesLimitDelete'] = F_OnFuturesLimitDelete
-- , ['OnInit'] = F_OnInit
, ['OnMoneyLimit'] = F_OnMoneyLimit
, ['OnMoneyLimitDelete'] = F_OnMoneyLimitDelete
, ['OnNegDeal'] = F_OnNegDeal
, ['OnNegTrade'] = F_OnNegTrade
, ['OnOrder'] = F_OnOrder
, ['OnParam'] = F_OnParam
, ['OnQuote'] = F_OnQuote
-- , ['OnStop'] = F_OnStop
, ['OnStopOrder'] = F_OnStopOrder
, ['OnTrade'] = F_OnTrade
, ['OnTransReply'] = F_OnTransReply
}
install_event_handling_functions (tbl_fun) -- Установка таблицы функций обработки событий фондового рынка ---
----
for i, v in next, condition_subscribe() do
message( 'Скрипт подписан на событие фондового рынка ' .. i )
end
--- Работа с событиями фондового рынка ----
-------------------
while IsRun do
---- Функция обработки всех событий QUIK: function handle_events(lim) -----
-- Параметр lim (по умолчанию равен 1000000) определяет максимальное количестко событий, обрабатаваемых за один вызов функции --
--- Если очереди событий пусты, то функция завершается.
-- Результат (их три): 1) количество обработанных событий в вызове функции; 2) максимальное количество обработанных событий за один вызов функции;
-- 3) максимальная задержка (в сек.) между записью и чтением событий (в вызове функции).
local event_counter, event_counter_max, read_latency_max = handle_events() ---- Обработка всех событий QUIK ----
if event_counter > 0 then -- В очередях были события ----
message ( 'event_counter: ' .. event_counter .. '; read_latency_max: ' .. read_latency_max )
end
---- Обработка в main остального -----
---- Конец Обработка в main остального -----
sleep(2)
end
------------------
end
function OnStop()
IsRun = false
DestroyTable(TableQUIK)
DestroyTable(TableQUIK1)
return 10000 -- !! По истечении этого интервала времени (в млсек.), данного скрипту на завершение работы, функция main() завершается принудительно.
-- При этом возможна потеря системных ресурсов. ! Если функция не выдает значение, то по умолчанию оно 5000 (5 сек.)
end
Pavel написал: на счете будет оставаться 500, всё что заработаю планирую выводить.
Вы знаете долларовых миллиардеров, заработавших свои состояния на внутридневной торговли на бирже? Банки, имеющие ресурсы, позволяющие нанимать совешенно продвинутных аналитиков фондового рынка и айтишников, и имеющие возможность создавать дочерние предприятия, занимающие внутридневной торговлей, предпочитают зарабатывать на комиссиях по обслуживанию такой торговли. Вас это не наводит ни на какие размышления? Если бы кто то мог гарантированно получать прибыль 15% годовых в долларах, мы бы это увидели.
nikolz написал: пора бы усвоить что халява - она и есть халява.Во-первых, весь квик - это официально бесплатный продукт.
Это вам так брокеры рассказывают, но при этом в ваших комиссиях брокеру незримо сидит некий процент отчисления за QUIK. Так что за QUIK пользователь платит и ARQA материально зависит от пользователей.
s_mike@rambler.ru написал: TGB, прекратите фантазировать.этот код (усложненный) работает в сотнях (может тысячах, я не знаю) копий роботов, из числа выложенных на сайте и написанных под заказ.
Вы что, никогда не сталкивались с ситуацией, когда программы использовались сотнями тысячами пользователями и в этих программах потом обнаруживались ошибки?
s_mike@rambler.ru написал: этот код (усложненный) работает в сотнях (может тысячах, я не знаю) копий роботов, из числа выложенных на сайте и написанных под заказ.
Я же написал, что, похоже, что код потокобезопасный. Но если вам все известно, то объясните, что происходит время от времени в коде Старателя?
Старатель написал: код в сообщении #24 в частности.
Выполненные мною в Lua 5.3 (5.4) тесты атомарности операций вставки/удаления полей в таблицу показали, что эти операции (в условиях реализован-ной многопоточности QLua) все-таки атомарны. Возможно?, есть ситуации, не охваченные тестированием, когда атомарности может не быть. Если же таких ситуаций нет, то мое утверждение, что выложенный Старателем код Queue (с описанием редко возникающей ошибки) создает потокоопасные очереди, ошибочное.
TGB написал: TGB написал:10000 обращений (запись, чтение, удаление) за 500 миллисекунд.Не смотря на то, что сам тест бестолковый, но холостой цикл за 0,5 сек не слишком ли много?
Опять неточность (мы же обсуждаем код Queue_safe). В моем обсуждаемом коде Queue_safe во фрагменте тестирования указано: -- 1. Этот фрагмент (использование потокобезопасных функций QLua) (100 000 записей и чтений) выполняется за ~3560 милисекунд --- ................ -- 2. Этот фрагмент (использование Queue_safe) (100 000 записей и чтений) выполняется за ~55 милисекунд ---
Старатель написал: И не увидел в вашем комментарии каких-то существенных отличий касательно использования метода pop().Больше похоже на спам вашего OS_...
Вы в комментарии https://forum.quik.ru/messages/forum10/message57219/topic6198/#message57219 читали это: 2. Существенная особенность построения очередей в OS_Quesha состоит в том, что они строятся на таблицах, в которых после их создания, нет вставок или удалений их полей (записей). В процессе работы с очередями изменяются только значения существующих полей, обеспечивающих запись/чтения этих очередей. Структура таблиц очередей «заморожена». Это обеспечивает в многопоточном режиме возможность эффективной синхронизации работы с очередями на уровне их полей, а не на глобальном уровне самих очередей. Так вот, эта существенная особенность обеспечивает то, что приведенный в комментарии конкретный код модуля Queue_safe создания очередей между колбеками и main является потокобезопасным в существующем QLua, в отличие от выложенного вами Queue, который потокобезопасным не является, о чем я уже писал в этой ветке.
Старатель написал: local hour = 0+os.date('%H') выскочила такая ошибка: attempt to perform arithmetic on a nil value
Ошибка, которую вы обнаружили, замечательна своей локализацией (всего одна строка кода). --- Я посмотрел в исходниках Lua 5.3 код функции os.date и то, как она вызывается на исполнение в коде Lua. Этот код не блокирует потоки в текущей версии QLua 5.3 (и это легко проверить). При этом, os.date, формально, не является C-функцией, так как в внутри ее кода, без блокировки, используется среда исполнения Lua (ее внутренние функции), формально не гарантирующая корректного параллелизма при ее использовании. Это же относится к некоторым другим библиотечным функциям Lua, имеющим признак C-функций. В C-функциях можно использовать только C-API Lua. Использование внутренних функции Lua, без блокировки, в коде C-функций является ошибкой. В версии Lua 5.3 (а возможно и в 5.4), похоже, есть «дыра» в некоторых ее стандартных функциях, для работы с ними из разных потоков. При работе в однопоточном варианте Lua такая «дыра» проявить себя не может. Но, собственно, разработчики Lua не один раз заявляли, что поддерживают однопоточный вариант Lua, а остальное факультатив, с которым надо разбираться тем, кто встраивает Lua в свои решения. ------ Конечно, можно попытаться анализировать все коды стандартных функций с используемыми внутренними функциями Lua без блокировки («раскручивая» соответствующие цепочки вызовов внутренних функций Lua) с целью выяснения, вдруг, случайно, какие то, из них, корректны при работе в нескольких потоках. Но по-хорошему, при написании всех кодов модулей библиотечных функции Lua, из ее среды исполнения, должно быть доступно только ее API (обеспечивающее корректность обращения к Lua из разных потоков). В принципе, для разработчика QUIK есть простой вариант исправления, в исходниках QLua, стандартных функций с используемыми внутренними функциями Lua без блокировки: в начале кода таких функций выполняется lua_lock, а при выходах из них (! их может быть несколько) выполняется lua_unlock.
Средства разработки многопоточных скриптов в QUIK., OS_Quesha, свидетельство регистрации в Роспатенте № RU 2020612905. Бесплатная для некоммерческого использования.
Владимир написал: Мне нет никакого дела до "проблем параллелизма в QLua" - мой скрипт считает, что он один такой работает, и про потоки и прочее знать ничего не знает - для него есть только прерывания от таймера, клавы или мыши.
Думать вы так, конечно, можете, а что же вы иногда жалуетесь на непонятные "падения" вашего скрипта? А может быть и кто то еще получает непонятные ситуации? Как системщик вы, наверное, должны понимать, что вам (нам) не обеспечена более-менее надежная среда разработки и исполнения скриптов. Вы что против того, чтобы надежность QLua повышалась? Если нет, то разработчик QUIK должен разбираться, с потоками, синхронизацией и т.д., часто, скрытыми API QUIK, но влияющими работу скриптов. И почему бы, если у кого есть такая возможность, не посодействовать ему, в этом, для нашей пользы?
Средства разработки многопоточных скриптов в QUIK., OS_Quesha, свидетельство регистрации в Роспатенте № RU 2020612905. Бесплатная для некоммерческого использования.
Владимир написал: вся задача организации торговли прикладная с головы до пят, и любые попытки привнести сюда системные штучки-дрючки - тем более, глубоко системные, как в данном случае, ни к чему хорошему не приведут.
Очень филосовское замечание Где вы видите системные штучки? Код написан на чистом Lua (даже циклов нет ). --- Я не понял ваших замечаний. Что в коде не так? Здесь выложен конкретный код, который, возможно, пригодится для устранения ситуации, описанной в комментарии: https://forum.quik.ru/messages/forum10/message54811/topic5823/#message54811 или для варианта модификации QLua, описанного в п.2 комментария: https://forum.quik.ru/messages/forum10/message57220/topic5823/#message57220 и освобождающего скриптера от проблем параллелизма в QLua, Этот код может оказаться интересным и еще кому-нибудь. При описании кода я коротко отметил его существенную особенность, Для кого-то из читателей будет понятно, почему это сделано так. Что в этом плохого? Вы же можете пользоваться своими "безразмерными" прикладными очередями или, например, использовать безразмерную (очень простую) очередь, код которой приведен в описании упомянутой мною ситуации. Но скорее всего, вам не нужны никакие очереди. Ну и замечательно.
Старатель написал: это уже вне зоны ответственности скриптера, а ошибка в реализации многопоточной модели QLUA.
1. Для устранения вашей конкретной ситуации, возможно, подойдет вариант реализации очередей, приведенный в моем комментарии https://forum.quik.ru/messages/forum10/message57219/topic6198/#message57219 2. Вообще, существует сравнительно несложный вариант заметно улучшить QLua (сделав его однопоточным для скриптера) без изменения его внутренней архитектуры: 1) добавить в API QLua функцию <задание схемы обработки колбеков>, которую скриптер может (но не обязательно) вызвать в начале своего скрипта с параметром задания схемы; если эта функция не вызвана, то колбеки обрабатывыются так, как это делается сейчас по старой схеме; дополнительно, в этой функции можно бы задавать версию Lua, а возможно и еще что-нибудь; при задании новой схемы, внутри QLua автоматически реализуется схема взаимодействия main с колбеками через две потокобезопасные эффективные очереди (как обеспечить их эффективность, наверное, понятно), одна из которых обслуживает колбеки фондового рынка, а вторая, колбеки событий таблиц QUIK, созданных скриптером; 2) при работе по новой схеме обработки колбеков, в API QLua добавить еще две функции: <функция подписки на события фондового рынка> и <функция подписки на события пользовательских таблиц >; в этих функциях скриптер может, в виде списка (в таблице) подписаться на события в соответствии с существующими именами колбеков; реализация подписки разработчиком QUIK может быть сведена к автоматическому созданию (скрытых) от пользователя функций колбеков, вызываемых, как это делается сейчас, и записывающих свои параметры с добавлением «от кого параметры» в соответствующие очереди; 3) для работы с очередями по новой схеме в API QLua добавить две функции чтения (?возможно также и выполнения некоторых дополнительных функций), соответственно очередям; при чтении очередей в считываемой таблице передаются: <имя события>, <время записи в очередь>, <параметры в том виде, как они описаны, в существующих колбеках>. При работе по второй схеме, QLua для скриптера становится однопоточным и снимаются многие сложные проблемы. Причем, остается возможность не затрагивать и тех скриптеров, которые по тем или иным причинам не станут уходить от старой схемы. ---- Общая схема работы скриптера в новой схеме обработки событий QUIK. <Заказывается новая схема обработки событий > <Выполняется подписка (вместо написания функций колбеков) на обрабатываемые события QUIK> <В функции main, в цикле читаются две очереди и обрабатываются считанные параметры событий> ------- Мною была описан некий, как мне представляется, не сильно сложный вариант реализации избавления QLua от проблем параллелизма, связанных с его существующей реализацией.
Средства разработки многопоточных скриптов в QUIK., OS_Quesha, свидетельство регистрации в Роспатенте № RU 2020612905. Бесплатная для некоммерческого использования.
Реализация очередей в OS_Quesha. 1. Кроме ранее описанного контроля, в системе выполняется периодический контроль текущих размеров всех ее очередей. При создании каждой очереди задается ее предельно-допустимый размер. Если текущий размер, какой то очереди превышает 75% предельно-допустимого, в системе начинает выдаваться об этом сообщение. 2. Существенная особенность построения очередей в OS_Quesha состоит в том, что они строятся на таблицах, в которых после их создания, нет вставок или удалений их полей (записей). В процессе работы с очередями изменяются только значения существующих полей, обеспечивающих запись/чтения этих очередей. Структура таблиц очередей «заморожена». Это обеспечивает в многопоточном режиме возможность эффективной синхронизации работы с очередями на уровне их полей, а не на глобальном уровне самих очередей. Далее выложен работающий код реализации очередей с учетом выше написанного для частного случая, когда один поток в нее пишет, а другой читает (!не допустимы чтения или записи в нескольких потоках):
Код
---- Реализация циклических потокобезопасных очередей без синхронизации для случая, когда один поток в нее пишет, а другой читает (!не допустимы чтения или записи в нескольких потоках)------
local Queue_safe = {
--- size_Queue - размер очереди ----
new = function (self, size_Queue)
local Queue = {}
local cycl = {} -- таблица-массив хранения элементов очереди
for i =1, size_Queue do
cycl[i] = 0
end
Queue.cycl = cycl
Queue.QueueLength = size_Queue --- размер очереди ---
Queue.RecordPointer = 1 --- указатель записи (изменяется только "писателем") ---
Queue.ReadingPointer = 1 --- указатель чтения (изменяется только "читателем") ---
Queue.written_down = 0 --- количество записанных элементов ---
Queue.read = 0 --- количество считанных элементов ---
return setmetatable(Queue, {__index = self}) --- для переключения на Queue_safe при обращение в созданной таблице к push, pop, size ----
end,
--- Записываемое в очередь значение не может быть nil --
-- Результат: 0 - запись выполнена; 1 - запись не выполнена (нет места)
push = function (self, v)
if v == nil then error(' !!! В очередь нельзя записывать nil ') end
if self.written_down - self.read >= self.QueueLength then ----- некуда писать ----
return (1)
end
local cycl = self.cycl
local pwt= self.RecordPointer
cycl [pwt] = v
local pwf = pwt +1
if pwf > self.QueueLength then
pwf=1
end
self.RecordPointer = pwf --- следующая позиция для записи (возможно занятая) --
self.written_down = self.written_down + 1
return (0)
end,
--- Два результата: 1) элемент или nil; 2) признак: 0 - очередь после чтения пуста; > 0 - количество непрочитанных элементов; -1 - очередь пуста в момент чтения;
pop = function (self)
local n = self.written_down - self.read
if n == 0 then
return nil, -1 --- нет данных
end
local cycl = self.cycl
local prt= self.ReadingPointer
local v = cycl [prt]
cycl [prt] = 0 --- чтобы не было "висячих" ссылок на ссылочные объекты ----
prt = prt +1
if prt > self.QueueLength then
prt = 1
end
self.ReadingPointer = prt
self.read = self.read + 1
return v, n - 1
end,
size = function (self)
return self.written_down - self.read
end
}
----------------------------------------------------------------------
------ Тесты -----
local T
local v
--[[
-- 1. Этот фрагмент (использование потокобезопасных функций QLua) (100 000 записей и чтений) выполняется за ~3560 милисекунд ---
T = {}
for j = 1, 100 do
for i = 1, 1000 do --- запись в очередь
table.sinsert (T, 'sd')
end
for i = 1, 1000 do --- чтение из очереди
v = T[1]
table.sremove (T, 1)
end
end
do return end ------
-----------------------------------------------------------------
--]]
-- 2. Этот фрагмент (использование Queue_safe) (100 000 записей и чтений) выполняется за ~55 милисекунд ---
T = Queue_safe:new(1200)
for j = 1, 100 do
for i = 1, 1000 do --- запись в очередь
T: push('sd')
end
for i = 1, 1000 do --- чтение из очереди
v = T: pop()
end
end
--------------------------------------------------------------
Относительно выложенного кода можно отметить следующее. 1. Это сильно упрощенный, работающий вариант реализации очередей, которые используются в OS_Quesha. 2. Особенностью этих очередей, при их создании, является необходимость задания их предельного размера. При записи в очередь, выдается признак успешности этой записи, либо отсутствия места в очереди. 3. Работа с создаваемыми очередями потокобезопасна без синхронизации при их использовании в QLua между колбеками и main. 4. Скорость выполнения операций запись/чтение не зависит от текущей длины очередей (в отличие от очередей, реализуемых с помощью table.sinsert и table.sremove, в которых скорость операций с увеличением текущей длины очереди, резко падает).
Старатель написал: Абсолютно беспочвенное утверждение, говорящее об отсутствии понимания работы обсуждаемого кода.
Вы имеете ввиду, что для записи и чтения в очередь используются свои указатели и, я бы с вами согласился, если бы не существовал общий объект, в котором это происходит (таблица хранения очереди в которую вставляются и из которой вычеркиваются элементы очереди). Вы уверены, что работа с таблицей при изменении ее структуры в Lua реализована потокобезопасно? Где это написано?
Старатель написал: В качестве разминки сделайте перезаказ обезличенных сделок (или переключитесь на другой сервер) ближе к концу торговой сессии и посмотрите, с какой задержкой будет обрабатываться ваша очередь и расход памяти скриптом.
Очередь не моя, а разработчика QUIK. Мне не известно как вы обрабатываете обезличенные сделки (сколько это может занимать ресурсов ПК), поэтому я запустил тест для проверки времени выполнения потокобезопасных функций реализации только очереди:
Код
---- Скрипт -тест ----
-- Этот фрагмент выполняется ~500 милисекунд ---
local T = {}
local v
for j = 1, 10 do
for i = 1, 1000 do --- запись в очередь
table.sinsert (T, {'sdfdfmffmf', 57})
-- v = T[1]
-- table.sremove(T, 1)
end
for i = 1, 1000 do --- чтение из очереди
-- table.sinsert (T, {'sdfdfmffmf', 57})
v = T[1]
table.sremove(T, 1)
end
end
10000 обращений (запись, чтение, удаление) за 500 миллисекунд. Понятно, что тест искуственный (без взаимодействия с колбеками), но какое то представление о скорости работы очереди и допустимой ее длине он дает. Заметного роста памяти QLua я не заметил.
Цитата
Старатель написал: Цитата TGB написал: В вашем коде, по вашей ссылке, используется в двух потоках потоконебезопасная очередь OnAllTrades. И?
Это значит, что при работе с такой очередью могут возникать ошибки синхронизации, которые могут порождать наблюдаемые вами последствия.
==========================
Цитата
_sk_ написал: Очередь, реализованная Старателем, в этом случае корректно работает и без блокировок.
Старатель написал: Старатель написал: local hour = 0+os.date('%H') выскочила такая ошибка: attempt to perform arithmetic on a nil value
Меня заинтриговала ошибка, которую вы обнаружили, в таком коротком коде. Напишите, пожалуйста: Как часто выполняется в скрипте приведенный вами фрагмент? Где это выполняется: в main или колбеках? -----------------------
Цитата
Старатель написал: Также ошибка attempt to call a nil value (method 'pop') в 9.1 никуда не пропала. Откуда здесь nil?
В вашем коде, по вашей ссылке, используется в двух потоках потоконебезопасная очередь OnAllTrades. В API QLua есть функции реализации потокобезопасной очереди, созданные разработчиком QUIK: table.sinsert, table.sremove. Пример использования потокобезопасной очереди: https://forum.quik.ru/messages/forum10/message56397/topic6356/#message56397
Об атомарности операций. Рассуждения об атомарности операции без определения ее области реализации это рассуждение о погоде без определения, к чему это относится (к Арктике или Антарктиде). Все операции скрипта, по отдельности, команды байт-кода или вызов C-функций являются атомарными, но в тоже время, выполнение скрипта, в целом, может быть и не атомарным (потоконебезопасным). Это связано с тем, что между упомянутыми операциями могут возникнуть переключение потоков, изменяющее контекст выполнение локально-атомарных операций. На самом деле, для выяснения потокобезопасности (атомарности выполнения скрипта в целом) достаточно выявить общие данные, модифицируемые в main и в основном служебном потоке QUIK, генерирующем вызовы колбеков скрипта. Если таких данных нет (а это часто можно обеспечить), то бессмысленно гадать об атомарности отдельных операций скрипта (скрипт выполняется атомарно, то есть, потокобезопасно, если нет таких данных).
Средства разработки многопоточных скриптов в QUIK., OS_Quesha, свидетельство регистрации в Роспатенте № RU 2020612905. Бесплатная для некоммерческого использования.
Дополнительно к тому, что было написано в моих предыдущих комментариях относительно OS_Quesha. 1. То, что написано в них, не относится к начальному «освоению» QLua. Это может оказаться интересным, наверное, тем, кто в этом «поварится» некоторое время, после которого проявятся те проблемы, о которых я пишу. 2. Моя позиция относительно любых средств разработки программ: они должны облегчать жизнь пользователя таких средств. Если есть возможность обеспечить такое, то пользователя надо, освобождать от сложных проблем параллельного программирования или обеспечения надежности разрабатываемых программ. 3. OS_Quesha, обеспечивая многопоточность скрипта, написанного на QLua, полностью (если не используются общие модифицируемые в нескольких потоках данные, для работы с которыми есть средства для «продвинутых» разработчиков) освобождают разработчика таких скриптов от проблем параллельного программирования и обеспечивает их надежность. 4. В продолжение моего комментария, в котором описаны средства обеспечения надежности скриптов QLua с использованием контрольных точек. Известными проблемами любых программ являются: 1) их возможные блокировки при обращении к разделяемым ресурсам (с синхронизацией доступа к ним); 2) их возможные зацикливания (из-за ошибок в них); 3) «утечка» памяти (из-за ошибок в них). Не существует универсальных, автоматических решений этих проблем. Уже хорошим решением может быть: ранняя диагностика таких проблем и это обеспечивается в OS_Quesha. В OS_Quesha выполняется контроль: 1) блокировок ее потоков; 2) зацикливания ее потоков; 3) утечки памяти QLua; 4) утечки памяти самого QUIK. Так как нормальные автоматические обработки описанных выше ситуаций мне не известны (они, как правило, связаны с существенными ошибками в скрипте, или QUIK), то по ним выдается четкая диагностика, а далее есть два варианта: продолжение работы скрипта (с продолжением выдачи упомянутой диагностики) или его перезапуск (возможно с перезапуском QUIK). В обобщенной классификации, OS_Quesha является «форком QLua», и в нем нет тех проблем, которые описаны в моем комментарии, относительно QLua, в соседней ветке «Отладка QUIK 8.13»; https://forum.quik.ru/messages/forum10/message57036/topic6356/#message57036
В предыдущих комментариях, при обсуждении конкретного, протестированого в непрерывном недельном прогоне, решения (в виде мизерной правки в 4-х местах исходников QLua), исправляющего, по моему мнению, существующую в QLua ошибку, было написано много текста, в котором можно «сломать голову».
На самом деле все достаточно просто. В QUIK запускается единственный служебный «основной» поток, который об-служивает колбеки всех запущенных скриптов пользователя и его созданных таблиц QUIK. В текущий момент существует, по крайней мере, два варианта, когда скриптер может, «случайно», выполнением кода своего скрипта, заблокировать «основной» поток (при этом перестают запускаться колбеки всех запущенных в QUIK пользователем скриптов и все скрипты «слепнут»: 1) скриптер написал непосредственно в запуске какого-то своего колбека длительно выполняющуюся функцию (почему бы нет?); 2) скриптер сумел написать в main длительно выполняющийся фрагмент байт-кода, в котором нет вызовов C-функций (почему бы нет?). Первый вариант лечится, если следовать рекомендациям разработчика QUIK по обработке колбеков. При этом, все что делается в main пользователя, становится однопоточным (относительно «основного» потока). «Основной» поток, при обработке колбеков, выполняет только короткие записи параметров колбекков в очереди. Причем, в этом случае, при написании и редактировании скрипта, заниматься синхронизацией с «основным» потоком не надо вообще. Второй вариант скриптер сейчас может лечить каждый раз, при редактировании скрипта, анализируя его текст на наличие фрагментов «длинных» байт-кодов и вставляя в них, в «?? нужных» местах какую-нибудь, функционально пустую C-функцию. Нужно ли это скриптеру, если это можно вылечить, внеся, предложенные мною, исправления в QLua? Могу ли быть ошибки, в предложенном мною? В любом коде ошибки могут быть, но это уже как-то протестировано (непрерывно в течении недель) да и разработчик QUIK может это потестировать.
Знаю. Так как есть исходники Lua. И вы можете узнать, никто не запрещает.
Цитата
Anton написал: Если бы вы это предложение выкатили команде луа, еще куда б ни шло, там хоть последствия понимают.
Команде Lua это не нужно. Как только вы встраиваете Lua в свою систему, за последствия отвечаете вы (почитайте в тексте исходников Lua комментарии; они же в свободном доступе).
Цитата
Anton написал: Еще раз, в текущем луа есть удобная особенность атомарности исполнения байткода. Вы это хотите сломать. Я против самой идеи это ломать.
Конечно, при этом исчезает интересная возможность многочасового гадания в коде скрипта на атомарность/неатомарность , если не следовать рекомендациям разработчика QUIK по взаимодействию с колбеками. Но самое интересное, даже, если вы убежденный противник рекомендаций разработчика Quik и не используете потокобезопасную очередь между вызовом колбеков и main, то, если нет общих данных, модифицируемых в вызовах колбеков и в функциях вызываемых в main, то гаданием атомарность/неатомарность можно, вообще, не заниматься.
Цитата
Anton написал: Как ее решить без ковыряния в кишках луа, я предложил.
Владимир написал: TGB , Дык я же давал чуть не десяток предложений в одном флаконе, и чуть ли не все они были зарегистрированы.
Вы за то, чтобы поток генерации колбеков блокировался фрагментами байт-кода? У вас есть конкретные замечания и возражения против моего конкретного протестированного предложения по устранению этих блокировок? Вы за то. чтобы был реализовано гипотетическое, "лабударно-завиральное", никак не проверенное предложение Anton, выдвинутое, как мне, но возможно ошибочно, кажется только для дискредитации того, что уже протестировано?
Я тоже свою систему почти не помню. Но вы же? в свое время? "нахлебались", по "полной", на таблицах QUIK (или нет). Вы что, хотите, чтобы и остальные пользователи получили "свое"? То, что вы просите, можно сделать самому. Вы, ведь, системный программист? Мне, просто бывает интересно (когда есть время), как "кипит" "социальный бульон" в текущее время.
Anton , зачем вы все время требуете продолжение «марлезонского балета»? Ну хорошо, продолжим.
Цитата
Anton написал: Я писал сначала, что не надо луа патчить для решения вашей проблемы?
На самом деле это проблема не моя. Для себя эту проблему я решил достаточно давно (аж в 2019г., когда разработал свою систему создания торговых роботов в QUIK). Утомлять вас здесь тем, как это у меня решено, я не буду.
Цитата
swerg написал: Я написал, как решить ее без патча луа? Написал. Еще что-нибудь?
Ну, наверное, вам бы стоило протестировать (на интервале, хотя бы, трех суток) предложенное вами решение у себя. И изложить его здесь с той же детальностью, как это сделал я. Наверное, из двух работающих решений можно бы выбрать то, которое лучше. Возможно, я бы и сам отказался от своего предложения. Я же не «упертый» и с дельными замечаниями, сделанными мне, легко соглашаюсь (можете посмотреть некоторые мои комментарии). А, кстати, почему вы так упорно боретесь против «форка lua»? Как то это сильно смахивает на борьбу за невинность девушки, которая уже больше года замужем.
Цитата
Anton написал: Нагуглить для вас критическую секцию с таймаутом или сами сумеете?
Этого делать не надо, а лучше поищите закладки в моем коде .
В Lua 5.4 у меня работает это. Внутри откомпилированного скрипта, в начале: <code> -- Функция --- local function dofile_exe_path() local str for i = 2, 4 do --- поиск корневой функции, вызывающей script_path, начиная с функции в которой вызвана script_path -- message (tostring( debug.getinfo(i) )) if debug.getinfo(i) then ---- str = debug.getinfo(i, 'S').source else break end end return str end
local path_script path_script = dofile_exe_path () message( path_script ) -- Результат message строка вида : dofile('<Путь к оттранслированному скрипту>')
Я уже думал, что наш диалог завершен, но вы пишите такие фантазийные, детективные тексты, что мне стало интересно.
Цитата
Anton написал: но тем, кто будет писать не как я, подложу-ка я закладочку и выкручивайтесь как выйдет". Вот так вот получается.
Где я такое написал? Если вам кажется, что это находится в опубликованном мною коде, то укажите, где вы нашли такую закладку?
Цитата
Anton написал: Я хорошо понимаю, чего вы добиться хотите
Вы что, экстрасенс ? Я же просто представил протестированный на своем стенде (на котором в сое время тестировал исправление ошибки синхронизации QLua) код исправления блокировок потоков длинными участками байт-кода. Это короткие правки в 4-х местах кода QLua. Их можно изучать визуально, проверять экспериментально, искать в них "до посинения" закладки.
Вот и замечательно! Хорошо построенная программная система должна быть защищенной.
Цитата
Anton написал: В то же время у нас есть виртуальная машина, поэтому арка может добавить отслеживание зависших скриптов, если вместо кондовой CRITICAL_SECTION в качестве лока будет использовать нечто иное с возможностью таймаута на вход. Если таймаут истек и секция не захвачена, весь скрипт убивается как в случае ошибки, все дела. В идеале сначала дергается некий юзерский колбек с вопросом "тебя прибить или сам отвиснешь", если ответил отвисну и не отвис, в морг. Вот что вам стоило бы попросить у арки, а не луа ломать.
И это предлагается вместо "прозрачных" правок в 4-х места кода, вносимых туда в течении пяти минут? Как-то очень сложно (я бы до такого не додумался ) и вот уж куда удобнее вставлять закладки.
Anton написал: теперь оказывается надо писать только так и никак иначе
Если вы это так поняли, то, придется, все-таки стоит пояснить. Конечно же, каждый разработчик может писать скрипты как хочет: 1) постоянно, после очередной редакции скрипта, анализируя код на его атомарность/неатомарность (марлезонский балет); 2) не заморачиваясь проблемама синхронизации с колбеками (вообще не зная что такое атомарность) И это никак не связано с тем, останется ли блокировка потоков участками байт-кода (как это про-исходит сейчас) или это будет устранено.
Anton написал: А на других примерах - произойдет. Это типичнейший паттерн вообще-то, как тут можно НЕ увидеть проблем...
Похоже, примеры с вами можно бы было обсуждать бесконечно ----
Цитата
Anton написал: Об чем и речь, весь код У ВСЕХ будет в этих костылях просто потому, что вам кровь из носу хотелось чонить в квике починить, пусть даже и не сломанное.
Опять декларации и страшилки. Но это же можно бы было сформулировать короче: «Не тронь!» Вы что, это не читали?:
Цитата
TGB написал: дело обстоит еще проще. Если вы знакомы с документом разработчика QUIK «Использование Lua в Рабочем месте QUIK.pdf», то его в разделе «2. Взаимодействие потоков Lua скрипта» описана рекомендуемая схема обработки колбеков фондового рынка, которую можно распространить и на обработку колбеков событий таблиц QUIK. Если придерживаться этой схемы, то проблем многопоточности (! из-за основного потока обработки колбеков) в скрипте пользователя не будет вообще: ни в «чистом» Lua-коде ни в смешанном. И не надо ни чем извращаться ни в одной строчке скрипта так как при этом он становится однопоточным (относительно основного потока обработки колбеков). Вы можете почитать комментарии, начиная с https://forum.quik.ru/messages/forum10/message56387/topic6356/#message56387 , в которых это обсуждается с демонстрацией кодов, рекомендуемых разработчикам QUIK. Если же в скрипте не используется схема обработки колбеков, рекомендованная разработчиком QUIK, то преобразование любого такого скрипта в функционально эквивалентный, но с рекомендованной схемой, элементарное.
или вы убежденный противник решения разработчика QUIK, приведенного в указанном фрагменте текста? Попытаюсь сформулировать, то, что написано там более понятно и короче. Если использовать то, что написано в этом фрагменте, то никакой синхронизации с потоком, обрабатывающем колбеки не требуется. Ни нужны никакие костыли. Не требуется многочасовой анализ кода скрипта на атомарность/неатомарность, (ворк/неворк ). Причем, это справедливо как для существующих версий QLua, так и в случае реализации предлагаемого мною решения, устраняющего ошибку блокировки потоков длинными участками байт-кода. Сформулировать это еще короче я уже, наверное, не смогу