Quik исполняет Lua скрипт на двух потоках. То, что внутри скриптовой функции main, исполняется на выделенном треде. Коллбеки OnXxxx исполняются на основном потоке Quik. Задача - гарантировать правильность последовательности вычислений, которые происходят внутри main и коллбеков. То есть, чтоб когда случился, например OnQuote, была бы гарантия, что исполняющийся параллельно main закончил свою часть вычислений и общие данные были бы в готовом состоянии. Примерный скрипт такой:
Код |
---|
data = {};
function update_ui()
Clear(table_id)
for i,v = in ipairs(data) do
InsertRow(table_id, ...)
end
end
function calculate_data_item(v)
-- Calculate new value based on v
-- Long, thread unsafe operation in real code, depends on other data[..] items
-- Adding 1 here only as example
return v + 1;
end
function update_all_data_items()
for i,v = in ipairs(data) do
data[i] = calculate_data_item(v);
end
end
function main()
-- Request level 2 quotes for each security
-- Init table, columns ...
while true do
-- START atomic operation on data array
update_all_data_items()
update_ui()
-- End atomic operation
sleep(1000)
end
end
function OnQuote(p1, p2)
-- START atomic operation on data[p2]
data[p2] = calculate_data_item(...)
update_ui()
-- End atomic operation on data[p2]
end
|
Там где в коментарии START - должна начинаться часть эксклюзивного кода, там где End - заканчиваться. Если организовать ожидание одним потоком пока завершит вычисления другой, то удается добиться полной корректности. Однако, некоторые функции Qlua (по всей видимости связанные с UI) блокируют исполнение, пока им не ответит основной тред Quik. В примере вызывается InsertRow и он внутри реализован так, что будет блокировать исполнение, пока ему не ответит основной тред Quik.
Происходит следующее:
1. main в точке START эксклюзивно запирает исполнение
2. Случается OnQuote и блокирует ожиданием основной поток Quik, пока исполнение заперто
3. В то же время main продолжает испололнение, благополучно завершает вычисление данных, подходит к вызову InsertRow
4. Внутри InsertRow происходит нечто, блокирующе зависящее от основного потока Quik
5. Основной поток Quik не откликается, потому что он в пункте 2 и ждет пока замок будет разлочен в main
6. main не может дойти до точки End, где замок разлочивается, потому что он ждет, пока InsertRow вернет исполнение
Получается, что внутри синхронизированного кода нельзя использовать ряд Qlua функций.
Вопросы:
1. Почему так происходит: InsertRow и подобные используют блокирующую отправку сообщений Windows (типа SendMessage вместо PostMessage) или это из-за логики локинга Quik? Понимание причины помогло бы придумать обходную схему
2. Какие функции поименно обладают таким же поведением как InsertRow? Понимание этого помогло бы писать скрипты так, чтоб хотя бы просто не натыкаться на эту проблему (не использовать внутри синхронизированного кода)