<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
	<channel>
		<title>Форум QUIK [тема: Синхронизация между потоком main и потоком, вызывающим OnXxx]</title>
		<link>http://forum.quik.ru</link>
		<description>Новое в теме Синхронизация между потоком main и потоком, вызывающим OnXxx форума  на сайте Форум QUIK [forum.quik.ru]</description>
		<language>ru</language>
		<docs>http://backend.userland.com/rss2</docs>
		<pubDate>Sat, 02 May 2026 14:28:27 +0300</pubDate>
		<item>
			<title>Синхронизация между потоком main и потоком, вызывающим OnXxx</title>
			<description><![CDATA[<b><a href="http://forum.quik.ru/messages/forum10/message27656/topic3206/">Синхронизация между потоком main и потоком, вызывающим OnXxx</a></b> в форуме <a href="http://forum.quik.ru/forum10/">Программирование на языке Lua</a>. <br />
			<br />====quote====<br />Когда ещё был старый форум, там это обсуждалось. Пусть кто-нибудь из представителей ARQA дальше по теме видимости изменения переменных отвечает (даёт ссылку на соответствующее обсуждение или заново тут ответит).<br /><br />Приведённый мною код работает с конца 2013 года без каких-либо проблем.<br />=============<br />Если то, что я предполагаю, верно, то даже в этом случае встретить ошибку шанс минимален. По сути, это классическая ситуация с многопоточностью в нативном приложении, когда есть два потока и одна переменная, допустим int, и даже если известно, что один поток только пишет, а второй только читает, можно обойтись без локинга, если есть гарантия, что операции чтения и записи - атомарны. То, есть на C++ формально нельзя делать так, хотя в реальной жизни наткнуться на последствия проблемы очень маловероятно:<br /><br />====quote====<br />int x;<br /><br />thread1_proc() {<br /> &nbsp;while (...) {<br /> &nbsp; &nbsp; if (x == 1) {<br /> &nbsp; &nbsp; &nbsp; ...<br /> &nbsp; &nbsp; }<br /> &nbsp;}<br />}<br /><br />thread2_proc() {<br /> &nbsp;while (...) {<br /> &nbsp; &nbsp;if (...) { <br /> &nbsp; &nbsp; &nbsp;x = 1;<br /> &nbsp; &nbsp;} else {<br /> &nbsp; &nbsp; &nbsp;x = ...<br /> &nbsp; &nbsp;}<br /> &nbsp;}<br />}<br />=============<br />Но можно делать, если x будет объявлен как std::atomic&lt;int&gt; x; потому что иначе простая строчка типа x = 1 или x == 1 компилируются в много инструкций, которые могут быть перемешаны, например, с частью инструкций от следующей строки С++ кода.<br />Поэтому действительно хочется услышать кого-нибудь из разработчиков, действительно они каким-то образом гарантируют атомарность присвоения и чтения всех переменных Lua. Задача эта, как мне кажется, нетривиальная. <br /><br /><br />====quote====<br />Конечно, так и надо делать. Только в стандартном Lua для этого нет средств, а в QLua, наверное, через sinsert как-то можно извратиться и реализовать Lock. На практике же повелось так, что в main-потоке большинство пишет цикл, совершающий нужные проверки/действия и засыпающий, если это надо/есть возможность.<br />=============<br />Все упирается в то, что в Lua в принципе нет исполнения одного контекста на разных реальных (таких, как тут) потоках. Не то что для этого нет средств, она в принципе не предназначена для того, чтоб с ней делали то, что сделано в Quik. Но нам даже из Lua можно сделать ровно то, что сделали разработчики Quik - использовать родные вызовы (например к WinApi) для работы с настоящими потоками. С их помощью можно сделать паузу в main (на что мы имеем право по документации) и уведомление о продолжении из колбеков. Единственное препятствие для такой схемы - то что некоторые вызовы, типа работы над табличным юзеринтерфейсом, блокирующие, в зависимости от ответа второго треда. Насчет этого требуется пояснения разработчиков, пока что приходится опытным путем устанавливать, какая у них логика локинга. <br />
			<i>13.11.2017 13:53:02, El El.</i>]]></description>
			<link>http://forum.quik.ru/messages/forum10/message27656/topic3206/</link>
			<guid>http://forum.quik.ru/messages/forum10/message27656/topic3206/</guid>
			<pubDate>Mon, 13 Nov 2017 13:53:02 +0300</pubDate>
			<category>Программирование на языке Lua</category>
		</item>
		<item>
			<title>Синхронизация между потоком main и потоком, вызывающим OnXxx</title>
			<description><![CDATA[<b><a href="http://forum.quik.ru/messages/forum10/message27651/topic3206/">Синхронизация между потоком main и потоком, вызывающим OnXxx</a></b> в форуме <a href="http://forum.quik.ru/forum10/">Программирование на языке Lua</a>. <br />
			Когда ещё был старый форум, там это обсуждалось. Пусть кто-нибудь из представителей ARQA дальше по теме видимости изменения переменных отвечает (даёт ссылку на соответствующее обсуждение или заново тут ответит).<br /><br />Приведённый мною код работает с конца 2013 года без каких-либо проблем.<br /><br />====quote====<br />Моя блокировка отличается от такой блокировке только тем, что разблокировка происходит <br />по уведомлению из другого треда напрямую непосредственно в момент, когда нужно произвести действия.<br />=============<br /><br />Конечно, так и надо делать. Только в стандартном Lua для этого нет средств, а в QLua, наверное, через sinsert как-то можно извратиться и реализовать Lock. На практике же повелось так, что в main-потоке большинство пишет цикл, совершающий нужные проверки/действия и засыпающий, если это надо/есть возможность. <br />
			<i>13.11.2017 11:36:27, _sk_.</i>]]></description>
			<link>http://forum.quik.ru/messages/forum10/message27651/topic3206/</link>
			<guid>http://forum.quik.ru/messages/forum10/message27651/topic3206/</guid>
			<pubDate>Mon, 13 Nov 2017 11:36:27 +0300</pubDate>
			<category>Программирование на языке Lua</category>
		</item>
		<item>
			<title>Синхронизация между потоком main и потоком, вызывающим OnXxx</title>
			<description><![CDATA[<b><a href="http://forum.quik.ru/messages/forum10/message27650/topic3206/">Синхронизация между потоком main и потоком, вызывающим OnXxx</a></b> в форуме <a href="http://forum.quik.ru/forum10/">Программирование на языке Lua</a>. <br />
			<br />====quote====<br />submit меняет только tail, get меняет только head. И указатели двигаются только в те моменты, когда структура готова. За видимость переменных из разных потоков разработчики QLua уже подумали<br /><br /><br /><br />=============<br />Операция присвоения не атомарна. В документации среди потокобезопасных функций она или аналоги не перечислена. То есть, по документации, можно быть уверенным что в середине<br />sinsert(...) не случится параллельного вызова, который обращается к той же структуре данных. А вот в середине self&#91;self.tail + 1&#93; = f он вполне можент случиться. По крайней мере я нигде<br />не нашел признаков того, что они на каждое присвоение производят синхронизацию между тредами. Это было бы убийственно для производительности, представляете, везде где встречается = нужно было<br />бы брать лок, делать операцию, снимать лок. По этой же причине, кстати, insert не сделан потокобезопасным, иначе бы на каждый его вызов приходилось бы все это дело блокировать/разблокировать, вместо<br />этого сделали безопасный sinsert, который бы пользователь вызывал только когда он действительно нужен.<br /><br />====quote====<br />Передавайте только те данные, которые нужны. Полагаю, что если использовать что-то блокирующее, у Вас производительность понизится. <br />=============<br />Производительность понижается в результате отсутствия блокировки. Потому что тогда приходится делать поллинг общей переменной - флага или очереди, как у вас. То есть, ее нужно в цикле внутри main опрашивать. <br />Если она опрашивается в цикле, то там будет блокировка, та же самая, только по таймеру (через функцию sleep). Моя блокировка отличается от такой блокировке только тем, что разблокировка происходит<br />по уведомлению из другого треда напрямую непосредственно в момент, когда нужно произвести действия. <br />
			<i>13.11.2017 11:08:43, El El.</i>]]></description>
			<link>http://forum.quik.ru/messages/forum10/message27650/topic3206/</link>
			<guid>http://forum.quik.ru/messages/forum10/message27650/topic3206/</guid>
			<pubDate>Mon, 13 Nov 2017 11:08:43 +0300</pubDate>
			<category>Программирование на языке Lua</category>
		</item>
		<item>
			<title>Синхронизация между потоком main и потоком, вызывающим OnXxx</title>
			<description><![CDATA[<b><a href="http://forum.quik.ru/messages/forum10/message27649/topic3206/">Синхронизация между потоком main и потоком, вызывающим OnXxx</a></b> в форуме <a href="http://forum.quik.ru/forum10/">Программирование на языке Lua</a>. <br />
			Код написан с учетом Ваших опасений<br /><br />====quote====<br />В коде выше чтение и запись в submit/get не синхронизированы. Что &nbsp;случится, если вызов будет произведен одновременно на двух родных &nbsp;потоках (мейн и колбек)? <br />Например, колбек вызовет submit, который уже модифицирует очередь &nbsp;при присвеоении, но еще не завершит операцию, а get уже начнет из нее &nbsp;читать?<br />=============<br /><br />submit меняет только tail, get меняет только head. И указатели двигаются только в те моменты, когда структура готова. За видимость переменных из разных потоков разработчики QLua уже подумали.<br /><br />====quote====<br />Все же, хотелось бы избежать копирования всего и вся из коллбеков в очередь.<br />=============<br /><br />Передавайте только те данные, которые нужны. Полагаю, что если использовать что-то блокирующее, у Вас производительность понизится.<br /><br />Как эвристику можно использовать следующее наблюдение: если при чтении из очереди она оказалась пуста, можно поставить sleep с аргументом побольше, если же непуста, то вообще не вызывать sleep.<br /><br />Если очередь разгребается быстрее, чем пополняется, то в памяти растёт только массив, на базе которого реализована очередь. Этот &nbsp;массив занимает в памяти относительно небольшой размер, а перезапуск скрипта, который происходит раз в несколько дней, приводит к старту с малого размера массива. <br />
			<i>13.11.2017 10:34:19, _sk_.</i>]]></description>
			<link>http://forum.quik.ru/messages/forum10/message27649/topic3206/</link>
			<guid>http://forum.quik.ru/messages/forum10/message27649/topic3206/</guid>
			<pubDate>Mon, 13 Nov 2017 10:34:19 +0300</pubDate>
			<category>Программирование на языке Lua</category>
		</item>
		<item>
			<title>Синхронизация между потоком main и потоком, вызывающим OnXxx</title>
			<description><![CDATA[<b><a href="http://forum.quik.ru/messages/forum10/message27648/topic3206/">Синхронизация между потоком main и потоком, вызывающим OnXxx</a></b> в форуме <a href="http://forum.quik.ru/forum10/">Программирование на языке Lua</a>. <br />
			Здравствуйте, спасибо за ответ.<br /><br />В коде выше чтение и запись в submit/get не синхронизированы. Что случится, если вызов будет произведен одновременно на двух родных потоках (мейн и колбек)?<br />Например, колбек вызовет submit, который уже модифицирует очередь при присвеоении, но еще не завершит операцию, а get уже начнет из нее читать?<br />В Lua скрипте такую схему можно было бы корректно реализовать при помощи функций, описанных в разделе &quot;Потокобезопасные функции для работы с таблицами Lua&quot; в qlua.chm.<br />На C++ подобную схему я делаю проще, с заведомо потокобезопасной структурой данных для очереди (как например тут <noindex><a href="https://github.com/elelel/qluacpp-tutorial/tree/master/log_all_trades" target="_blank" rel="nofollow">https://github.com/elelel/qluacpp-tutorial/tree/master/log_all_trades</a></noindex> ).<br />Все же, хотелось бы избежать копирования всего и вся из коллбеков в очередь. Если обработку производить в main, нужно будет саспендить этот поток (единственный<br />не созданный нами поток, который мы имеем право саспендить) и пробуждать по мере надобности. Но для этого надо знать условия, когда его можно саспендить, чтоб не<br />происходило блокировки обоих ниток по сценарию как оригинальном посте. <br />
			<i>13.11.2017 10:25:19, El El.</i>]]></description>
			<link>http://forum.quik.ru/messages/forum10/message27648/topic3206/</link>
			<guid>http://forum.quik.ru/messages/forum10/message27648/topic3206/</guid>
			<pubDate>Mon, 13 Nov 2017 10:25:19 +0300</pubDate>
			<category>Программирование на языке Lua</category>
		</item>
		<item>
			<title>Синхронизация между потоком main и потоком, вызывающим OnXxx</title>
			<description><![CDATA[<b><a href="http://forum.quik.ru/messages/forum10/message27645/topic3206/">Синхронизация между потоком main и потоком, вызывающим OnXxx</a></b> в форуме <a href="http://forum.quik.ru/forum10/">Программирование на языке Lua</a>. <br />
			Один из вариантов кардинально решить проблему многопоточности при программировании на QLua описан ниже.<br /><br />В потоке main делаем всю логику торгового робота, а из потока коллбэков только передаём данные в поток main через очередь. Очередь решает задачу синхронизации, коллбэки завершают свою работу максимально быстро, не тормозя UI терминала.<br /><br />Реализация очереди функций на базе массива позволяет в потоке коллбэков указать, какие данные нужно использовать, и как именно. При получении функции в потоке main остаётся лишь запустить её.<br />
====code====
<pre>--
-- Реализация очереди из функций и их исполнения.
--

local Executor = {}

--- Конструктор.
-- @param self объект
local function new(self)
&nbsp;&nbsp;&nbsp;&nbsp;local queue = {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;head = 1,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tail = 0
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;setmetatable(queue, self)
&nbsp;&nbsp;&nbsp;&nbsp;self.__index = self
&nbsp;&nbsp;&nbsp;&nbsp;return queue
end

Executor.new = new

--- Поместить функцию в очередь на выполнение.
-- @param self объект
-- @param f функция
local function submit(self, f)
&nbsp;&nbsp;&nbsp;&nbsp;if type(f) == "function" then
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self&#91;self.tail + 1&#93; = f
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.tail = self.tail + 1
&nbsp;&nbsp;&nbsp;&nbsp;end
end

Executor.submit = submit

--- Получить очередную функцию из очереди.
-- @param self объект
-- @return функция
local function get(self)
&nbsp;&nbsp;&nbsp;&nbsp;if self.head &#62; self.tail then
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return nil
&nbsp;&nbsp;&nbsp;&nbsp;else
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;local f = self&#91;self.head&#93;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self&#91;self.head&#93; = nil
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.head = self.head + 1
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return f
&nbsp;&nbsp;&nbsp;&nbsp;end
end

Executor.get = get

--- Выполнять функции из очереди либо пока они там есть, либо пока не будет выполнено указанное количество функций.
-- @param self объект
-- @param max максимальное количество исполняемых за один раз функций
local function execute(self, max)
&nbsp;&nbsp;&nbsp;&nbsp;max = max or 1000000
&nbsp;&nbsp;&nbsp;&nbsp;while max &#62; 0 do
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;local f = self:get()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if f == nil then
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;f()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;max = max - 1
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end
&nbsp;&nbsp;&nbsp;&nbsp;end
end

Executor.execute = execute

return Executor</pre>
=============
<br />Используется это примерно так.<br />
====code====
<pre>local tradeCounters = {} -- таблица&#91;secCode&#93;, содержащая количество обезличенных сделок по каждому инструменту</pre>
=============
<br />В потоке коллбэков пишем, например:<br />
====code====
<pre>function OnAllTrade(t)
&nbsp;&nbsp;&nbsp;&nbsp;executor:submit(function()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tradeCounters&#91;t.sec_code&#93; = (tradeCounters&#91;t.sec_code&#93; or 0) + 1
&nbsp;&nbsp;&nbsp;&nbsp;end)
end</pre>
=============
<br />В потоке main пишем цикл, <I>достаточно часто</I> вызывающий функцию execute, т.е. что-то типа:<br />
====code====
<pre>while not interrupted do
&nbsp;&nbsp;&nbsp;&nbsp;....
&nbsp;&nbsp;&nbsp;&nbsp;executor:execute()
&nbsp;&nbsp;&nbsp;&nbsp;....
end</pre>
=============
В результате в потоке main выполнится функция, которую мы задали в потоке коллбэков с данными, которые в тот момент были доступны.<br /><br />Удачи! <br />
			<i>13.11.2017 07:29:07, _sk_.</i>]]></description>
			<link>http://forum.quik.ru/messages/forum10/message27645/topic3206/</link>
			<guid>http://forum.quik.ru/messages/forum10/message27645/topic3206/</guid>
			<pubDate>Mon, 13 Nov 2017 07:29:07 +0300</pubDate>
			<category>Программирование на языке Lua</category>
		</item>
		<item>
			<title>Синхронизация между потоком main и потоком, вызывающим OnXxx</title>
			<description><![CDATA[<b><a href="http://forum.quik.ru/messages/forum10/message27641/topic3206/">Синхронизация между потоком main и потоком, вызывающим OnXxx</a></b> в форуме <a href="http://forum.quik.ru/forum10/">Программирование на языке Lua</a>. <br />
			Quik исполняет Lua скрипт на двух потоках. То, что внутри скриптовой функции main, исполняется на выделенном треде. Коллбеки OnXxxx исполняются на основном потоке Quik. Задача - гарантировать правильность последовательности вычислений, которые происходят внутри main и коллбеков. То есть, чтоб когда случился, например OnQuote, была бы гарантия, что исполняющийся параллельно main закончил свою часть вычислений и общие данные были бы в готовом состоянии. Примерный скрипт такой:
====code====
<pre>data = {};

function update_ui()
&nbsp;&nbsp; Clear(table_id)
&nbsp;&nbsp; for i,v = in ipairs(data) do
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;InsertRow(table_id, ...)
&nbsp;&nbsp; end
end

function calculate_data_item(v)
&nbsp;&nbsp; -- Calculate new value based on v
&nbsp;&nbsp; -- Long, thread unsafe operation in real code, depends on other data&#91;..&#93; items
&nbsp;&nbsp; -- Adding 1 here only as example
&nbsp;&nbsp; return v + 1;
end

function update_all_data_items()
&nbsp;&nbsp; for i,v = in ipairs(data) do
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;data&#91;i&#93; = calculate_data_item(v);
&nbsp;&nbsp; end
end

function main()
&nbsp;&nbsp; -- Request level 2 quotes for each security
&nbsp;&nbsp; -- Init table, columns ... 
&nbsp;&nbsp; while true do
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-- START atomic operation on data array
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;update_all_data_items()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;update_ui()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-- End atomic operation
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sleep(1000)&nbsp;&nbsp; 
&nbsp;&nbsp; end
end

function OnQuote(p1, p2)
&nbsp;&nbsp; -- START atomic operation on data&#91;p2&#93;
&nbsp;&nbsp; data&#91;p2&#93; = calculate_data_item(...)
&nbsp;&nbsp; update_ui()
&nbsp;&nbsp; -- End atomic operation on data&#91;p2&#93;
end

</pre>
=============
Там где в коментарии START - должна начинаться часть эксклюзивного кода, там где End - заканчиваться. Если организовать ожидание одним потоком пока завершит вычисления другой, то удается добиться полной корректности. Однако, некоторые функции Qlua (по всей видимости связанные с UI) блокируют исполнение, пока им не ответит основной тред Quik. В примере вызывается InsertRow и он внутри реализован так, что будет блокировать исполнение, пока ему не ответит основной тред Quik. <br /><br />Происходит следующее:<br />1. main в точке START эксклюзивно запирает исполнение<br />2. Случается OnQuote и блокирует ожиданием основной поток Quik, пока исполнение заперто<br />3. В то же время main продолжает испололнение, благополучно завершает вычисление данных, подходит к вызову InsertRow<br />4. Внутри InsertRow происходит нечто, блокирующе зависящее от основного потока Quik<br />5. Основной поток Quik не откликается, потому что он в пункте 2 и ждет пока замок будет разлочен в main<br />6. main не может дойти до точки End, где замок разлочивается, потому что он ждет, пока InsertRow вернет исполнение<br /><br />Получается, что внутри синхронизированного кода нельзя использовать ряд Qlua функций. <br />Вопросы:<br />1. Почему так происходит: InsertRow и подобные используют блокирующую отправку сообщений Windows (типа SendMessage вместо PostMessage) или это из-за логики локинга Quik? Понимание причины помогло бы придумать обходную схему<br />2. Какие функции поименно обладают таким же поведением как InsertRow? Понимание этого помогло бы писать скрипты так, чтоб хотя бы просто не натыкаться на эту проблему (не использовать внутри синхронизированного кода) <br />
			<i>12.11.2017 13:40:04, El El.</i>]]></description>
			<link>http://forum.quik.ru/messages/forum10/message27641/topic3206/</link>
			<guid>http://forum.quik.ru/messages/forum10/message27641/topic3206/</guid>
			<pubDate>Sun, 12 Nov 2017 13:40:04 +0300</pubDate>
			<category>Программирование на языке Lua</category>
		</item>
	</channel>
</rss>
