function OnStop()
run = nil
SetCell(id, 1, 1, string.format('Size: %u', GetTableSize(id))) -- Почему-то попадаем в строку 2
end
local alltrade
function OnAllTrade()
if not run then return end
alltrade = true
for i = 1, 30 do
DeleteRow(id, 1)
InsertRow(id, 30)
end
end
function main()
id = AllocTable()
AddColumn(id, 1, 'Size', true, QTABLE_INT_TYPE, 15)
CreateWindow(id)
SetWindowPos(id, 0, 0, 100, 520)
for i = 1, 30 do
InsertRow(id, -1)
end
run = true
while run do
if alltrade then
alltrade = nil
SetCell(id, 30, 1, string.format('%u', GetTableSize(id)))
else sleep(1) end
end
end
Через некоторое время работы появляется 31 строка. Первая строка становится недоступна для редактирования.
Проблема вызвана тем, что работа с таблицей в скрипте осуществляется из разных потоков без синхронизации.
Сценарий проблемы следующий: - OnAllTrade вызывается из основного потока программы. В ней скрипт выполняет очистку таблицы; - одновременно в функции main(), работающей в потоке скрипта, вызывается функция SetCell, которая обновляет строку; - возможна ситуация, при которой строка уже удалена, но новая не вставлена. Если SetCell сработает между этими вызовами, то, так как строка уже удалена и не найдена, она будет добавлена вновь.
Evgeniy Karnaukhov написал: так как строка уже удалена и не найдена, она будет добавлена вновь.
Зачем?
Цитата
Evgeniy Karnaukhov написал: Проблема вызвана тем, что работа с таблицей в скрипте осуществляется из разных потоков без синхронизации.
Можете предложить рабочий пример? Такой вариант тоже не работает:
Код
function OnAllTrade()
if not run then return end
alltrade = true
for i = 1, rows do
table.ssort({0, 0}, function()
DeleteRow(id, 1)
InsertRow(id, rows)
return true
end)
end
end
Надо делать так, как надо. А как не надо - делать не надо.
Кажется понял, почему виснет. Мейн у нас в отдельном потоке, работа с таблицей так или иначе приводит к SendMessage окну (в основной поток то есть), и собственно все, основной поток стоит на критической секции в колбеке и обработать сообщение не может. Если так, надо либо из мейна в окно не лазить, либо из колбеков (всех). Сиречь либо редиректить события в мейн и лазить только оттуда, либо лазить только из колбеков. Ну либо хачить со всеми вытекающими.
В связи с этой проблемой возникла такая мысль, а что если нам попросить такую фичу: функция для вызова пользовательского колбека в основном потоке и соответствующий колбек. То есть например
с простой реализацией: первая посылает сообщение главному окну со ссылкой на произвольный юзерский тип, главное окно по получении сообщения дергает колбек с полученным значением и возвращенное значение возвращает из сообщения. Если посылаем из мейна - мейн встает и ждет, пока в главном потоке выполнится колбек, причем это "даром", за счет свойств винды. Если посылаем из другого колбека - колбек выполняется синхронно, тоже за счет свойств винды. Это хоть какая-то синхронизация была бы, а то ж сейчас ее нет от слова совсем. Приглашаю накидать аргументов против, если кому интересно.
Evgeniy Karnaukhov написал: Если SetCell сработает между этими вызовами, то, так как строка уже удалена и не найдена, она будет добавлена вновь.
Зачем?
Цитата
Evgeniy Karnaukhov написал: возможна ситуация, при которой строка уже удалена, но новая не вставлена.
Пример без InsertRow:
Код
rows = 20
function OnParam()
if not run then return end
rows = rows - 1
DeleteRow(id, 1)
if rows == 10 then run = nil end
end
function main()
id = AllocTable()
AddColumn(id, 1, 'row', true, QTABLE_INT_TYPE, 15)
CreateWindow(id)
SetWindowPos(id, 0, 0, 110, 350)
for i = 1, rows do InsertRow(id, -1) end
run = true
while run do
SetCell(id, 5, 1, tostring(rows))
sleep(1)
end
for row = 0, GetTableSize(id) do
SetCell(id, row, 1, tostring(row))
end
end
Результат:
Обсуждаемая проблема может возникнуть в любом скрипте, в котором в одном потоке пишутся данные в таблицу, а в другом удаляется строка из начала/середины таблицы. Предложение Антона создать пользовательский колбек выглядит разумным. Да и в других ситуациях такой колбек не помешал бы.
Надо делать так, как надо. А как не надо - делать не надо.
Я уже давно ушел от редактирования чего либо в колбеках Квика. Есть глобальная очередь сообщений, в колбеке в нее идет только запись информации о событии.
А уже в main идет обработка и очистка. Также реализованы свои колбеки - задача в очередь, когда выполнилась дернул свою функцию. Многопоточность - это опять решать проблемы потоков. Блокировка поможет, но тогда зачем потоки, если блокировать остальные.
Цитата
Старатель написал: Что-то никак не пойму, работа с QLua-таблицей - это синхронные или асинхронные сообщения?Саппорт, можете просветить?
Предположу что синхронный. Квик виснет если сделать бесконечный цикл в колбеке окна.
Старатель написал: При вызове InsertRow / DeleteRow в какой момент происходит смещение индексов?
В смысле?
Что есть DeleteRow (InsertRow)? Это не только удаление (добавление) физической строки, но и смещение всех индексов строк во внутреннем представлении. Ожидается, что операция смещения строк - цельная. Т.е., если выполнить код в таком порядке: