Работа функций OnStop() и SetCell()

Страницы: 1
RSS
Работа функций OnStop() и SetCell(), Подвисает скрипт
 
Всем привет. Имеется простой скрипт:
Код
IsRun = true

function main()

   S_TAB = AllocTable()
   AddColumn(S_TAB, 1, "Param",true, QTABLE_STRING_TYPE, 18)
   CreateWindow(S_TAB)
   InsertRow(S_TAB, 1)

   while IsRun do
      sleep(1000)
   end

   SetCell(S_TAB, 1, 1, "Stopped")
   Message('Script stopped')

end

function OnStop()
   IsRun = false
end
Если вызывается функция OnStop(), то скрипт здорово подвисает при выполнении SetCell(). Подвисает настолько, что код после SetCell не успевает выполняться, т.к. заканчивается таймаут. Причём какой бы большой таймаут ни был, всё что после SetCell не выполняется. Это можно как-то исправить или побороть?
Если SetCell разместить внутри OnStop, то скрипт не зависает. Но мне нужно, чтобы SetCell была в Main. Если завершать работу скрипта другим способом (например IsRun = false в любом другом месте), то скрипт также не зависает, и SetCell и весь остальной код выполняется до конца.
Может есть какое-то ограничение, о котором я не знаю просто, и нельзя использовать функции для работы с таблицами, когда срабатывает OnStop?

Версия Квик 8.8.4.3

Не то, чтобы это была критичная проблема, но уж очень много времени я потратил на её поиск :)
 
Здравствуйте, Сергей.

Описываемое Вами поведение, формально, не является проблемой или ошибкой в работе lua-скриптов в терминале.

Дело в том, что в текущей реализации интерпретатора LUA в терминале QUIK выполнение функции SetCell, как и обработка события OnStop происходят в основном потоке.
В результате этого, функция SetCell и код, следующий за ней не выполняются после завершения обработки OnStop.

В качестве возможного решения - предлагаем вынести SetCell в OnStop.
 
Andrey Bezrukov, Андрей, Ваша рекомендация "вынести SetCell в OnStop" есть фактически признание того, что такой поведение ЯВЛЯЕТСЯ ошибкой в работе lua-скриптов.

Это была, кажется, первая ошибка, на которую я напоролся, придя сюда. Давал соответствующее предложение, которое, кажется, даже было зарегистрировано. Сейчас я сформулировал бы это так: "НЕЛЬЗЯ выносить в обработчик любые действия, которые не могли бы быть выполнены в основном потоке". Или лучше заменить "нельзя" на "категорически не рекомендуется". OnStop должен сбрасывать флаг бесконечного цикла в main - И ВСЁ! Более того: всё это дело УЖЕ прекрасно работает, если флаг останова сбрасывается в любом другом месте, кроме OnStop.
 
Вообще странно, ведь, из описания OnStop следует, что эта функция для того и нужна, чтобы дать время для корректного завершения функции Main.
Ну ладно, думаю, надо просто принять к сведению, что после бесконечного цикла в Main не следует размещать какие-то важные или критичные части скрипта. :)
 
Лучше при штатной работе скриптов вообще не использовать OnStop, которая возникает при нажатии кнопки "Остановить", слишком много проблем от этого может возникнуть. Считайте, что это -- аварийный способ завершения скрипта. У себя я сделал для каждого из скриптов с GUI специальное окошко, при закрытии которого происходит поднятие флага прерывания и скрипт, периодически проверяя его, должен в разумное время завершить работу и освободить ресурсы. Да, это костыль, но зато не прерывается выполнение потока коллбэков и всё ещё работает штатно.
 
Цитата
Andrey Bezrukov написал:
Описываемое Вами поведение, формально, не является проблемой или ошибкой в работе lua-скриптов в терминале.

Это несоответствие описанного в документации поведения программы, в значит - ошибка.
Плюс,  конечно, совершенно кривая и топорная реализация ожидания завершения main(), когда  (из документации)
Цитата
"РМ QUIK может некоторое время находиться в «подвисшем» состоянии"

Итак, открываем документацию, файл "Использование Lua в Рабочем месте QUIK.pdf".

На картинке "Событийная модель" после OnStop мы видим фразу:
Цитата
"Прекращение вызова обработчиков событий терминала QUIK".

Однако далее в тексте мы видим другое объяснение:
Цитата
"Также необходимо иметь в виду, что функция OnStop() выполняется в основном потоке РМ QUIK, и,
так как в момент ожидания завершения скрипта (5 секунд) он занимает основной поток РМ QUIK, то
другие обработчики событий уже не вызываются
, а само РМ QUIK может некоторое время
находиться в «подвисшем» состоянии."

Т.е. это уже не "мы сознательно сделали так, чтобы обработчики не вызывались", а "ну, блин, ну так во вышло, что по случайному стечению обстоятельств из-за имеющейся реализации обработчики вызываться перестают". Между "мы сознательно так сделали" и "ну вот так вот получилось" - есть большая разница.

Окей, я продолжаю далее следовать рекомендациям из того же описания (официального).
Чуть ранее:
Цитата
Возможные подходы написания скриптов Lua для плагина QLua в Рабочем месте QUIK
....
2. Вся необходимая логика описывается в функции с предопределенным именем main().

Супер. Такой подход нам и демонстрирует автор изначального сообщения этой темы. Он все делает в main(). Использует лишь один обработчик, связанный с интерфейсом пользователя OnStop(), т.к. надо же скрипту сообщить "остановись, хватит". Ибо иного пути нам не предоставлено.

И тут вдруг выясняется, что в этом описанном в документации подходе есть "особенности", когда часть функций попросту не работают в каких-то ситуациях! Как это так??
Это явная ошибка, которую необходимо исправить.
Да, обработчики событий не должны вызываться после OnStop, в этом есть логика, да и это явно описано в документации. Но другие-то функции взаимодействия с терминалом почему не должны работать?? потому что "так получилось"? Нет уж, это не есть обоснование; это лишь признание кривоватой реализации данного места, которая, очевидно, является ошибкой и должна быть исправлена.
 
Цитата
swerg написал:
Это явная ошибка, которую необходимо исправить.Да, обработчики событий не должны вызываться после OnStop, в этом есть логика, да и это явно описано в документации. Но другие-то функции взаимодействия с терминалом почему не должны работать?? потому что "так получилось"? Нет уж, это не есть обоснование; это лишь признание кривоватой реализации данного места, которая, очевидно, является ошибкой и должна быть исправлена.
 Присоединяюсь к коллеге.
1. Непонятно, почему таблицы QUIK обслуживаются в основном потоке обработки колбеков. В чем проблема сделать обслуживание таблиц QUIK в отдельном специальном для этого потоке?
2. Зачем завершение скрипта пользователя выполняется в потоке обработки колбеков? Наверное, в OnStop() было бы достаточно выставлять признак завершения скрипта, а все работу по завершению скрипта пользователя выполнять в потоке main.
 
Так же присоединяюсь ко всему этому негодованию.

Еще раз приведу цитату из документации (которую уже тут приводили)
Цитата
Также необходимо иметь в виду, что функция OnStop() выполняется в основном потоке РМ QUIK, и,
так как в момент ожидания завершения скрипта (5 секунд) он занимает основной поток РМ QUIK, то
другие обработчики событий уже не вызываются, а само РМ QUIK может некоторое время
находиться в «подвисшем» состоянии.
Зачем OnStop() вообще занимает основной поток пока ожидается завершение скрипта и не дает выполняться другим функциям в основном потоке?
Вот реально зачем это надо??

Достаточно чтобы OnStop() выполнился и передал в QUIK сигнал, что через столько-то миллисекунд требуется завершить принудительно скрипт, если он еще вдруг не завершен. Все.
А как корректно завершить свой скрипт, пользователь сам разберется.
Не надо нам мешать в этом.

Прошу разработчиков либо объяснить, зачем OnStop() делает то, что от него совершенно не требуется, либо зарегистрировать пожелание на приведение OnStop() к адекватному поведению.
 
Цитата
TGB написал:
работу по завершению скрипта пользователя выполнять в потоке main.
 Уточнение:
 Для случаев зацикливания или блокировок в завершаемом скрипте пользователя, в основном потоке (а лучше, в специальном служебном потоке, чтобы не отвлекать основной поток от обслуживания колбеков, возможно, других работающих скриптов пользователя) контролировать завершение скрипта. Если за время по умолчанию или явно указанное в OnStop() скрипт не завершен, то выполняется принудительное его завершение, но это, скорее всего, будет происходить редко.
 
Цитата
TGB написал:
1. Непонятно, почему таблицы QUIK обслуживаются в основном потоке обработки колбеков. В чем проблема сделать обслуживание таблиц QUIK в отдельном специальном для этого потоке?
Это как раз понятно: лезть в объекты интерфейса Windows и связанных с этим собственных структур данных из разных потоков - слишком сложная замута. Потому её всегда избегают.

Цитата
TGB написал:
а все работу по завершению скрипта пользователя выполнять в потоке main.
Сам себя поток прибить не может. Должен кто-то "извне".

Цитата
TGB написал:
2. Зачем завершение скрипта пользователя выполняется в потоке обработки колбеков?
Не совсем так.
Очевидно, что написан обычный стандартный код (который выполняется в основном потоке):
Код
вызвать OnStop()
подождать завершения потока main() или таймера на 5 сек   <<-- вот тут проблема
if сработал таймер -> прибить поток для main()


Т.к. идет просто ожидание без процессинга очереди сообщений Windows и прочих действий - то и получаем, что и терминал в это время висит, и функции, которые через основной поток выполняются, не рабтают.

Это бы развязать, конечно. "Но эт вряд ли".
 
Цитата
swerg написал:
Сам себя поток прибить не может. Должен кто-то "извне".
Цитата
TGB написал:
Уточнение:  Для случаев зацикливания или блокировок в завершаемом скрипте пользователя, в основном потоке (а лучше, в специальном служебном потоке, чтобы не отвлекать основной поток от обслуживания колбеков, возможно, других работающих скриптов пользователя) контролировать завершение скрипта. Если за время по умолчанию или явно указанное в OnStop() скрипт не завершен, то выполняется принудительное его завершение, но это, скорее всего, будет происходить редко.
-------------------
Цитата
swerg написал:
Это бы развязать, конечно. "Но эт вряд ли".
 На самом деле, устранение обсуждаемых здесь ошибочных ситуаций дело разработчика QUIK. Как это реализовать пусть он решает. Что надо устранить вы описали детально и этого, наверное, достаточно.
 
swerg, TGB, BlaZed, добрый день!

Как уже было сказано ранее, такова реализация работы OnStop() на текущий момент, это не является ошибкой.
Просьба более подробно описать, как Вы хотели бы видеть логику работы функции в данном месте. Готовы зарегистрировать пожелание на доработку.
 
Roman Azarov,
Цитата
Как уже было сказано ранее, такова реализация работы OnStop() на текущий момент, это не является ошибкой.
А вот КТО, простите, решает, что является ошибкой, а что нет? Вам уже ГОДАМИ на все лады твердят, что это именно ошибка - и что толку? Я бы даже сказал, НЕПРИЛИЧНАЯ ошибка. Эту ветку цитировать не буду, а фрагменты своих разговоров приведу "в дополнение":

03.07.2021 16:31:47
Andrey Bezrukov, Андрей, Ваша рекомендация "вынести SetCell в OnStop" есть фактически признание того, что такой поведение ЯВЛЯЕТСЯ ошибкой в работе lua-скриптов.
Это была, кажется, первая ошибка, на которую я напоролся, придя сюда. Давал соответствующее предложение, которое, кажется, даже было зарегистрировано. Сейчас я сформулировал бы это так: "НЕЛЬЗЯ выносить в обработчик любые действия, которые не могли бы быть выполнены в основном потоке". Или лучше заменить "нельзя" на "категорически не рекомендуется". OnStop должен сбрасывать флаг бесконечного цикла в main - И ВСЁ! Более того: всё это дело УЖЕ прекрасно работает, если флаг останова сбрасывается в любом другом месте, кроме OnStop.

19.06.2021 19:08:23
1. Почти мгновенно после того, как я появился здесь и попробовал написать свой первый скрипт, я столкнулся с ситуацией потери управления по OnStop. Антон тогда подробно расписал мне, что происходит и почему, и с тех пор у меня в самом этом обработчике сидит запись результатов, закрытие лога, убийство таблиц и прочее, но ведь это же ненормально! Согласитесь, весьма неприятно потерять результаты всего дня только потому, что мы имели неосторожность "не вовремя" обратиться к функциям основного потока вроде SetCell.
Пожелание: после остановки бесконечного цикла передавать управление следующему за циклом оператору в main - так, как это и происходит, если флаг isRun сбрасывается не в OnStop, а в любом другом месте.

Andrey Bezrukov
23.06.2021 14:11:56
1. Ваше пожелание зарегистрировано. Мы постараемся рассмотреть его и сообщить Вам результаты анализа.

Ну и?
 
Цитата
Roman Azarov написал:
Как уже было сказано ранее, такова реализация работы OnStop() на текущий момент, это не является ошибкой.

Ограничения, не указанные в документации - есть ошибка.

Цитата
Roman Azarov написал:
Просьба более подробно описать, как Вы хотели бы видеть логику работы функции в данном месте. Готовы зарегистрировать пожелание на доработку.

Регистрируйте.
1. В документацию включить список функций, которые нельзя вызывать из OnStop() по каким-либо причинам, как нарушающие корректность выполнения программ Lua или работу терминала.
2. После этого у вас будет список функций с ограничением по функционалу. Очевидно, далее следует работать над уменьшением размера этого списка, т.е. надо добавлением возможности всё же вызывать функции из этого списка из OnStop() (c постепенным удалением таковых функций из данного неприятного списка).
 
Вариант решения выше озвученных пожеланий:
1) поток, обслуживающий колбеки событий фондового рынка, занимается только ими (и ни чем другим);
2) добавляется отдельный поток запуска/завершения скриптов и обслуживания таблиц QUIK;
3) для защиты потоков от возможной, взаимной блокировки кодами на «чистом» Lua, реализуется решение (состоящее из конкретных правок в 4-х местах исходного текста QLua), которое было приведено в комментарии https://forum.quik.ru/messages/forum10/message56351/topic6356/#message56351
В чем проблемы реализации такого варианта?
В своей системе реализации многопоточных скриптов OS_Quesha я запускаю столько потоков, сколько мне требуется и они работают нормально.
Страницы: 1
Читают тему (гостей: 1)
Наверх