Добрый день, Всем С момента появления VMLua в КВИКЕ неоднократно рассказывал о том, что применение event вместо sleep не только экономит ресурсы процессора, но и позволяет не пропускать события в колбеках и максимально бысто на них реагировать. говорил об этом например здесь:
--------------------- К сожалению, кроме бессмысленного хамства некоторых посетителей форума, ничего конкретного никто не написал. --------------- Но вот наконец-то появился вменяемый чел . и после моей попытки в очередной раз объяснить преимущество event
он все же решил проверить это и убедился, что это так действительно:
Код
Просто проверка концепции:
Кодw32 = require("w32")
run = true
evt = false
function OnInit()
evt = w32.CreateEvent(nil, 0, 0, nil)
end
function OnStop()
run = false
w32.SetEvent(evt)
end
function main()
while run do
w32.WaitForSingleObject(evt, 1000000)
end
w32.CloseHandle(evt)
end
В колбеках вызываете SetEvent - main сразу просыпается.
С чем Всех и поздравляю.
Пользователь
Сообщений: Регистрация: 23.12.2023
28.04.2024 13:05:04
Присоединяюсь. Не нужно иметь много образования чтобы понять что техника с ожиданием событий эффективна. А реализация не так сложна, как кажется на первый взгляд.
Пользователь
Сообщений: Регистрация: 31.01.2015
01.05.2024 11:30:29
Эту возможность надо в квик добавить.
Пользователь
Сообщений: Регистрация: 20.03.2023
02.05.2024 15:33:16
Для моих целей сообщения тоже оказались заметно быстрее, чем sleep. На скрине для каждой пары неродных для квика стаканов левый отрисован с ожиданием через сообщения, правый - через sleep. Внизу стаканов статистика потерь, они на правых стаканах больше. Kernel time и cpu time тоже меньше на потоке, который ждёт событий. Поскольку рисование стаканов идёт в main скрипта, нельзя использовать WaitForMultipleObjects для событий из OnQuote и OnStop, нужно MsgWaitForMultipleObjectsEx, чтобы main включался на событиях GUI для стаканов. Её нет в w32, пришлось подключать через cffi-lua. Инструкция по сборке написана . Если сравнивать время исполнения между w32 и cffi-lua, то конечно же w32 быстрее (SetEvent на 12%, CreateEvent на 23%). Важные части кода:
Код
local w32 = require "w32-ext"
local ffi = require "cffi"
local SetEvent = w32.SetEvent
local MsgWaitForMultipleObjectsEx = w32.MsgWaitForMultipleObjectsEx
local cb_t = {'EDM4','SiM4','CRM4','CNYRUB_TOM'}
local e_quote, e_stop
local is_run = true
function OnQuote(class_code, sec_code)
local sent = cb_t[sec_code]
if sent then
cb_t[sec_code] = sent + 1
SetEvent(e_quote)
end
end
function OnStop()
is_run = false
SetEvent(e_stop)
end
function main()
e_stop = w32.CreateEvent(nil, 0, 0, nil)
e_quote = w32.CreateEvent(nil, 0, 0, nil)
local events = ffi.new("HANDLE[2]", {e_quote, e_stop})
local WAIT_TIMEOUT = w32.WAIT_TIMEOUT
while is_run do
local res = MsgWaitForMultipleObjectsEx(2, events, 500, 7423, 0) -- QS_ALLINPUT == 7423
if res == 1 then break end
if res == WAIT_TIMEOUT then
-- по таймауту обработать сообщения, например, о смене foreground окна
else
-- обновить стаканы
end
end
end
end
w32-ext.lua:
Код
local w32 = require("w32")
local ffi = require "cffi"
ffi.cdef [[
typedef unsigned int UINT;
typedef unsigned long DWORD;
typedef long long LRESULT;
typedef long long LPARAM;
typedef unsigned long long WPARAM;
//typedef void* HWND; // in windows
typedef intptr_t HWND; // uintptr_t not ok as it appends 'ULL', LPARAM also ok
typedef intptr_t HANDLE;
void SetLastError(DWORD);
//LRESULT SendMessageA(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
HWND GetForegroundWindow();
DWORD MsgWaitForMultipleObjectsEx(DWORD nCount, const HANDLE *pHandles, DWORD dwMilliseconds, DWORD dwWakeMask, DWORD dwFlags);
]]
w32.SetLastError = ffi.C.SetLastError
w32.GetForegroundWindow = ffi.C.GetForegroundWindow
w32.MsgWaitForMultipleObjectsEx = ffi.C.MsgWaitForMultipleObjectsEx
-- those calls trigger 127 error on first invocation
w32.SetLastError(0)
return w32
и оба коллбэка OnAllTrade и OnQuote почти одновременно выдали
Код
SetEvent(evt)
При вторичной выдаче SetEvent(evt) эта установка события не выполнится? Теперь вызывается main и начинает их обрабатывать событие: она проверяет и обрабатывает общую очередь от обоих коллбэков. Это правильная работа с событиями?
Как надо по-минимуму изменить параметры CreateEventA, WaitForSingleObject и SetEvent, чтобы main знала, какой именно коллбэк выдал SetEvent(evt)?
Пользователь
Сообщений: Регистрация: 27.12.2022
10.06.2024 12:15:04
И чтобы событие создавалось обоими коллбэками, если даже они это сделали почти одновременно.
Пользователь
Сообщений: Регистрация: 27.12.2022
10.06.2024 12:24:27
Насколько я понял, надо использовать параметр lpName в CreateEventA: коллбэки создают события со своими именами. Осталось уточнить: что будет, если оба коллбэка успеют создать события, а main ещё не запустилась для их обработки. Не будет ли потеряно второе событие?
Пользователь
Сообщений: Регистрация: 30.01.2015
10.06.2024 13:11:49
Цитата
Serge123 написал: Насколько я понял, надо использовать параметр lpName в CreateEventA: коллбэки создают события со своими именами. Осталось уточнить: что будет, если оба коллбэка успеют создать события, а main ещё не запустилась для их обработки. Не будет ли потеряно второе событие?
createEvent создает не событие, а объект "событие) упрощенно это значит, что OS выделяет для флага события ячейку и вернет его имя Имя флага всегда уникально. ----------------- Далее мы либо устанавливаем Set флаг или сбрасываем Reset. Wait.. - это как бы аналог условного цикла проверки состояния флага c заданием времени ожидания. Проверку делает OC а поток бездействует пока Wait ------------ Если событий много то используем wait дя нескольких событий либо более сложные функции ожидания (семафоры и т д) ================= Если есть желание изучить механизмы синхронизации потоков рекомендую книгу:
Пользователь
Сообщений: Регистрация: 27.12.2022
10.06.2024 13:35:50
Насколько я понял, т.к. в моём случае параметр dwFlags у CreateEventA равен 0, то ResetEvent не требуется. Похоже, когда Виндовс по установленному коллбэком флагу события запускает main, то Виндовс сбрасывает этот флаг, поэтому в принципе возможна потеря возникновения события. Ясно что события не буферизуются. Ожидание множества событий тут по-моему не очень подходит, т.к. main должна отрабатывать при возникновении хотя бы одного события.
Такую книжку Рихтера (только в синей ламинированной обложке) я купил давно, тогда я ещё не программировал на Си. Пока что меня не интересует общая теория семафоров, светофоров и шлагбаумов: я сейчас конкретно переписываю скрипт с Луа на Си dll, отвлекаться на эксперименты неохота. Пока у меня обработка событий на Си происходит в коллбэках и я собираюсь перенести её в main с использованием очереди и объектов событий, как это у меня работает в Луа.
Пользователь
Сообщений: Регистрация: 27.12.2022
10.06.2024 14:24:49
Насколько я понял из , Виндовс не суммирует установку событий во флаге события, если несколько раз вызвать SertEvent. Поэтому main должна проверять, не был ли вызван другой коллбэк, установка события от которого могла быть потеряна.
Пользователь
Сообщений: Регистрация: 27.12.2022
10.06.2024 15:30:30
Тут возникла новая проблема: если обработчик OnAllTrade на Си уже не будет обрабатывать этот вызов, а будет что-то записывать в очередь и делать выход, то как гарантировать, что память под таблицу alltrade не будет освобождена до обработки этой таблицы в функции main, которая тоже находится в dll? Совсем отключить сборку мусора, или есть что-то ещё?
Пользователь
Сообщений: Регистрация: 30.01.2015
11.06.2024 06:57:44
Цитата
Serge123 написал: Тут возникла новая проблема: если обработчик OnAllTrade на Си уже не будет обрабатывать этот вызов, а будет что-то записывать в очередь и делать выход, то как гарантировать, что память под таблицу alltrade не будет освобождена до обработки этой таблицы в функции main, которая тоже находится в dll? Совсем отключить сборку мусора, или есть что-то ещё?
могу лишь рассказать как я решил эту проблему. У меня колбеки обрабатываются не только в main, но и в других, новых потоках. Так как все колбеки вызываются последовательно, то нет надобности делать много Event У меня один event (см мои скрипты на форуме) и очередь. Т е вызвал терминал колбек. В колбеке в очередь записывается номер колбека и входные параметры. Если очередь пустая, то устанавливается флаг события. В функции main , если очередь пустая wait ждет событие, иначе проводится обработка очереди. Если колбеки не требуют сложных вычислений, то они обрабатываются внутри main. Для сложной обработки, например , в портфеле множество бумаг, то для принятия решения для каждой бумаги вызывается новый поток из пула потоков ОC. В этом потоке я запускаю LuasJIT (могу запустить python, terra,julia и др) вместо lua, что обеспечивает ускорение вычислений на порядок, по сравнению с вычислениями в main.
Пользователь
Сообщений: Регистрация: 27.12.2022
11.06.2024 10:23:05
Цитата
nikolz написал: могу лишь рассказать как я решил эту проблему.
К сожалению, в этом ответе не сказано о решении этой проблемы... А именно: как в main (которая сидит в dll) обработать таблицу alltrade, если при завершении работы OnAllTrade (которая тоже сидит в dll) стек её вызова (в котором сидит ссылка на табл. alltrade) должен очиститься? Я вижу такие костыли: - запретить сборку мусора (до 18:45). - dll из OnAllTrade как-то связывается с Lua и Lua дублирует ссылки на alltrade в массив локальных переменных области видимости этого файла lua. Потом dll как-то даёт знать, что каким-то из этих переменных можно присвоить nil. Как это сделать, пока не знаю. М.б. это делается через (light)userdata, но я с этим не связывался.
Как из Си заставить Lua продублировать ссылку на таблицу, если эта ссылка сидит в стеке? Как из Си вызвать скрипт Lua, чтобы он получил этот стек вызова OnAllTrade? Иначе придётся коллбэки делать на Lua и только main на Си, а этого не хочется из-за какой-то потери скорости.
Пользователь
Сообщений: Регистрация: 27.12.2022
11.06.2024 10:59:50
Имел в виду, что для МЕНЯ это не решение проблемы, т.к. я не знаю, как применять LuaJIT и прочие штуки...
написал: могу лишь рассказать как я решил эту проблему.
К сожалению, в этом ответе не сказано о решении этой проблемы... А именно: как в main (которая сидит в dll) обработать таблицу alltrade, если при завершении работы OnAllTrade (которая тоже сидит в dll) стек её вызова (в котором сидит ссылка на табл. alltrade) должен очиститься? Я вижу такие костыли: - запретить сборку мусора (до 18:45). - dll из OnAllTrade как-то связывается с Lua и Lua дублирует ссылки на alltrade в массив локальных переменных области видимости этого файла lua. Потом dll как-то даёт знать, что каким-то из этих переменных можно присвоить nil. Как это сделать, пока не знаю. М.б. это делается через (light)userdata, но я с этим не связывался.
Как из Си заставить Lua продублировать ссылку на таблицу, если эта ссылка сидит в стеке? Как из Си вызвать скрипт Lua, чтобы он получил этот стек вызова OnAllTrade? Иначе придётся коллбэки делать на Lua и только main на Си, а этого не хочется из-за какой-то потери скорости.
Попробую объяснить. ------------------ Не важно, что и где сидит. dll - это код программы. Для кода не нужна синхронизация потоков. Синхронизация потоков нужна при обращении к данным. ---------------- Попробую снова объяснить как Ваша проблема решается на моей программе. ОЧЕРЕДЬ и EVENT - эти элементы и решают проблему. ------------------ Поясняю на примере: 1) Терминал вызывает OnAllTrade и передает в стеке указатель на таблицу, которая является новой строкой таблицы alltrade. -------------------- 2) В OnAllTrade указатель на полученную таблицу новой строки записывается в очередь Cохраняется в другой таблице КАРЛ ! ! ----------------------- 3) OnAlltrade заканчивает работу. Если очередь при этом содержит лишь один элемент, то устанавливается флаг EVENT. ---------------------------------- 4) Если функция main была в состоянии WAIT, то она начинает обрабатывать элемент очереди И о чудо, Карл!!! там есть указатель на таблицу новой строки alltrade, при этом onAllTrade давно уже завершилась или может быть даже снова вызвана с новой таблицей новой строки. Ты понимаешь, Карл? Снова хоть тысячу раз!!! -------------------------- Если функция main обрабатывала очередь, то она и продолжает это делать, а новые вызовы onAllTrade подкидывают новые таблицы новых строк alltrade в очередь. -------------------------- И это все. Нет никаких проблем в работе моей программы, Осталась лишь тебе понять то, что я написал Карл!!!
Пользователь
Сообщений: Регистрация: 27.12.2022
13.06.2024 10:36:27
Цитата
nikolz написал: Нет никаких проблем в работе моей программы,Осталась лишь тебе понять то, что я написал Карл!!!
написал: Нет никаких проблем в работе моей программы,Осталась лишь тебе понять то, что я написал Карл!!!
Я не Карл... Это уже какая-то патология...
Тогда не волнуйтесь. Я вам раньше уже ответил, что мой вариант решает вашу проблему. Но Вы стали утверждать обратное, так как ничего не поняли. -------------------- Я на вашем примере написал ликбез для карла и других читателей. Вы можете это не читать. Я же Вас не заставляю, а Вы меня не нанимали. --------------------- Вместо "спасибо" за то, что я вам разжевал бесплатно что-то и в рот положил, вы надулись как рыба луна. вот это действительно патология. ---------------------------- Я просто погулять вышел, ты понял Карл!!! .