Появляется лишняя строка в таблице

Страницы: 1
RSS
Появляется лишняя строка в таблице
 
Демонстрационный скрипт:
Код
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
 
Цитата
Старатель написал:
Такой вариант тоже не работает
А если в мейне тоже в критическую секцию завернуть?
 
Цитата
Anton написал:
в мейне тоже в критическую секцию завернуть
QUIK повесится
 
Цитата
Старатель написал:
QUIK повесится
Кажется понял, почему виснет. Мейн у нас в отдельном потоке, работа с таблицей так или иначе приводит к SendMessage окну (в основной поток то есть), и собственно все, основной поток стоит на критической секции в колбеке и обработать сообщение не может. Если так, надо либо из мейна в окно не лазить, либо из колбеков (всех). Сиречь либо редиректить события в мейн и лазить только оттуда, либо лазить только из колбеков. Ну либо хачить со всеми вытекающими.
 
В связи с этой проблемой возникла такая мысль, а что если нам попросить такую фичу: функция для вызова пользовательского колбека в основном потоке и соответствующий колбек. То есть например
Код
ANYTYPE SendCustomNotification(ANYTYPE v)
ANYTYPE OnCustomNotification(ANYTYPE v)
с простой реализацией: первая посылает сообщение главному окну со ссылкой на произвольный юзерский тип, главное окно по получении сообщения дергает колбек с полученным значением и возвращенное значение возвращает из сообщения. Если посылаем из мейна - мейн встает и ждет, пока в главном потоке выполнится колбек, причем это "даром", за счет свойств винды. Если посылаем из другого колбека - колбек выполняется синхронно, тоже за счет свойств винды. Это хоть какая-то синхронизация была бы, а то ж сейчас ее нет от слова совсем. Приглашаю накидать аргументов против, если кому интересно.
 
Цитата
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

Результат:


Обсуждаемая проблема может возникнуть в любом скрипте, в котором в одном потоке пишутся данные в таблицу, а в другом удаляется строка из начала/середины таблицы.
Предложение Антона создать пользовательский колбек выглядит разумным. Да и в других ситуациях такой колбек не помешал бы.
 
Цитата
Anton написал:
работа с таблицей так или иначе приводит к SendMessage окну (в основной поток то есть)
Что-то никак не пойму, работа с QLua-таблицей - это синхронные или асинхронные сообщения?
Саппорт, можете просветить?
 
Я уже давно ушел от редактирования чего либо в колбеках Квика. Есть глобальная очередь сообщений, в колбеке в нее идет только запись информации о событии.

А уже в main идет обработка и очистка. Также реализованы свои колбеки - задача в очередь, когда выполнилась дернул свою функцию.
Многопоточность - это опять решать проблемы потоков. Блокировка поможет, но тогда зачем потоки, если блокировать остальные.

Цитата
Старатель написал:
Что-то никак не пойму, работа с QLua-таблицей - это синхронные или асинхронные сообщения?Саппорт, можете просветить?
Предположу что синхронный. Квик виснет если сделать бесконечный цикл в колбеке окна.
 
Цитата
Старатель написал:
синхронные или асинхронные
Функции же возвращают что-то, в случае PostMessage было бы нельзя результат получить. Но как на самом деле тоже послушал бы.
 
Цитата
Старатель написал:
синхронные или асинхронные

Синхронные
 
Откуда лишняя строка в примере #9?
 
Старатель,
По этой теме Вам уже был дан ответ, строка появляется из за обращения к таблице из разных потоков.
 
Цитата
Sergey Gorokhov написал:
Синхронные
При вызове InsertRow / DeleteRow в какой момент происходит смещение индексов?

Цитата
Evgeniy Karnaukhov написал:
Проблема вызвана тем, что работа с таблицей в скрипте осуществляется из разных потоков без синхронизации.
Это чья зона ответственности, скриптера или приложения?
 
Цитата
Старатель написал:
При вызове InsertRow / DeleteRow в какой момент происходит смещение индексов?
В смысле?

Цитата
Старатель написал:
Это чья зона ответственности, скриптера или приложения?

Со стороны QUIK, нет синхронизации.
 
Цитата
Sergey Gorokhov написал:
Цитата
Старатель написал:
При вызове InsertRow / DeleteRow в какой момент происходит смещение индексов?
В смысле?
Что есть DeleteRow (InsertRow)? Это не только удаление (добавление) физической строки, но и смещение всех индексов строк во внутреннем представлении.
Ожидается, что операция смещения строк - цельная. Т.е., если выполнить код в таком порядке:
Код
[callback] InsertRow(id, 1)
[main] SetCell(id, 1, 1, "что-то")
то "что-то" ожидаю увидеть в первой строке, а не чёрт знает где.
Если порядок вызова такой:
Код
[main] SetCell(id, 1, 1, "что-то")
[callback] InsertRow(id, 1)
то запись должна быть либо во второй строке, либо её не должно быть вообще, если таблица на момент SetCell не имела строк.

Что за фантом появляется в виде нулевой строки - не понятно. Отговорки типа "патамушта, мы так захотели" - не объяснение.

Цитата
Sergey Gorokhov написал:
Со стороны QUIK, нет синхронизации.
Дайте скриптеру инструмент синхронизации. Но баг с нулевой строкой в любом случае надо чинить.
Страницы: 1
Читают тему (гостей: 1)
Наверх