Если бы я был архитектором QUIK

Страницы: Пред. 1 2
RSS
Если бы я был архитектором QUIK, Что стоило бы изменить в QUIK по-крупному
 
Вопрос к поддержке:
   В QUIKе много различных коллбеков. Где гарантии, что они не пропускают события, по которым должны выполняться?
Пропуск событий в заявленных коллбеках, даже если это происходит нечасто, это ошибка QUIK.
   В существующей версии QUIK коллбеки выполняются в единственном служебном потоке.  Что происходит в QUIKе если возникает очередное новое событие, по которому должен выполниться коллбек и при этом не был обработан предыдущий? Есть служебные очереди необработанных событий?
   В существующей версии QUIK выполнение коллбека блокируется до тех пор пока в потоке main не выполнится sleep или сишная функция.
   Выше написанное означает полную зависимость служебного потока от пользовательского. Зачем это сделано в QUIKe?
   В этой ветке были изложены предложения, которые, по моему мнению, могли бы устранить описанные выше проблемы. Что в этих предложениях не понятно или вызывает сомнения?
 
Цитата
TGB написал:
В существующей версии QUIK выполнение коллбека блокируется до тех пор пока в потоке main не выполнится sleep или сишная функция.     Выше написанное означает полную зависимость служебного потока от пользовательского. Зачем это сделано в QUIKe?
Блокируется на время выполнения байт-кода. Не представляю, какой код нужно написать без вызова сишных функций, выполняющийся длительное время, чтобы это можно было заметить.
 
Функция  sleep  уступает свободное время потока следующему потоку.
Например sleep(1000) в main означает, что поток main будет остановлен системой на 1000 ms.
-----------------------
Таким образом,
функция sleep выполняется  быстро, так как ее задача сообщить ОС
чтобы та разбудила поток через заданное время.
ОC устанавливает таймер на событие "запустить поток main через 1000 ms.
------------------------
Все эти действия выполняются буквально мкс .
Поэтому ничего не блокируется для исполнения sleep.
 
чтобы остановить выполнение потоко с колбеком надо в функцию колбека поставить sleep.
Поток колбека будет остановлен на время указанное в sleep.
 
и еще...
Если значение аргумента sleep равно нулю, поток освобождает оставшуюся часть своего интервала времени для любого потока с таким же приоритетом, готовым к выполнению.
Если других готовых к выполнению потоков с таким же приоритетом нет, выполнение текущего потока не приостанавливается.
 
Цитата
nikolz написал:
Поэтому ничего не блокируется для исполнения sleep.
    Когда же вы научитесь читать :smile: ? Вы читаете тексты перед тем как писать?
  Ведь написано:
Цитата
TGB написал:
блокируется до тех пор пока в потоке main не выполнится sleep или сишная функция
 Где вы видите у меня фразу: "для исполнения"?  
 У вас какое то недержание ваших текстов.
 
Цитата
TGB написал:
Цитата
nikolz написал:
Поэтому ничего не блокируется для исполнения sleep.
     Когда же вы научитесь читать :: ? Вы читаете тексты перед тем как писать?
  Ведь написано:
Цитата
TGB написал:
блокируется до тех пор пока в потоке main не выполнится sleep или сишная функция
  Где вы видите у меня фразу: "для исполнения"?  
 У вас какое то недержание ваших текстов.
Вы тоже не умеете читать.
Что именно по-вашему блокируется  "пока в потоке main не выполнится sleep"  
Напишите конкретно от какого до какого момента исполнения кода в функции Main блокируется
--------------------
Я написал именно на это ваше высказывание.
Ничего не блокируется "до тех пор пока в потоке main не выполнится sleep или сишная функция"
===================
Поясняю специально для Вас:
Поток может останавливается в многопоточной системе  если он обращается к ПАМЯТИ  ДАННЫХ ,
к которой обращается в данный момент другой поток. И то если потоки должны писать в эту память.
Если они читатели то никакой блокировки никто не делает.
---------------------------------------
Теперь скажите  К КАКОЙ ПАМЯТИ ВСЕГДА одновременно обращаются колбек и поток main  для записи данных в эту память
"до тех пор пока в потоке main не выполнится sleep или сишная функция"
--------------------------------------
 
TGB,
Если Вы знаете русский язык, то фраза
"до тех пор пока в потоке main не выполнится sleep или сишная функция"
означает, что время измеряется от некоторого момента до выполнения sleep  до момента окончания выполнения sleep.
-----------------
И понять эту фразу очень сложно.
Время до оператора  sleep     -   это время обработки очереди в цикле в main (пример есть в документации).  
Если бы в это время колбеки остановились, то никакой очереди создать было бы невозможно.
--------------------
момент времени после исполнения sleep   Если в sleep 0, то это практически время начала исполнения sleep  B этом случае оно совпадает с началом цикла обработки очереди.
-----------------------------
таким образом Вы сказали буквально следующее:  Колбеки всегда остановлены если в main нет функции sleep или параметр у нее равен нулю.
Но колбеки прекрасно работают и без sleep в main.
----------------------------
Согласитесь, что вы сказали чушь.
 
Цитата
nikolz написал:
Что именно по-вашему блокируется  "пока в потоке main не выполнится sleep"  Напишите конкретно от какого до какого момента исполнения кода в функции Main блокируется
    Вы опять не читаете или ничего не соображаете.  У меня же написано конкретно то, что давно известно:
Цитата
TGB написал:
В существующей версии QUIK выполнение коллбека блокируется до тех пор пока в потоке main не выполнится sleep или сишная функция.
   Выполните в main строку: for i = 0, 10000000000 do  end
  и вы заблокируете секунд на 30 не только коллбеки, но и диалог QUIK.
  -----
Цитата
nikolz написал:
Вы сказали буквально следующее:  Колбеки всегда остановлены если в main нет функции sleep или параметр у нее равен нулю.
   Вы опять меня "передергиваете".  Где я это писал буквально?  Читайте мою приведенную выше цитату. Вы понимаете что означает слово буквально?
 Зачем вы меня "передергиваете" и пытаетесь приписать свои фантазии?
 Вы продолжаете работать прокладкой между комментариями :smile: ?
 
В песочнице QUIK 12.8.3.4 была создана заявка с номером 10256702148.
Она не удаляется ни вручную ни из скрипта.
 Выдается сообщение: "Не удалось снять заявку с номером 10256702148"
 Распечатка заявки из таблицы orders:
<code>
{--таблица: 18.02.26 13:04:52:795 - время создания.
  ['lseccode'] = [=[]=]
, ['qty'] = 7.0
, ['activation_time'] = 0
, ['accepted_uid'] = 0
, ['revision_number'] = 0
, ['sec_code'] = [=[YDEX]=]
, ['investment_decision_maker_short_code'] = 0
, ['visible_repo_value'] = 0.0
, ['accruedint'] = 0.0
, ['executing_trader_qualifier'] = 0
, ['trading_session'] = 0
, ['account'] = [=[NL0011100043]=]
, ['client_qualifier'] = 0
, ['operation_type'] = 0
, ['min_qty'] = 0.0
, ['ext_order_flags'] = 0
, ['client_code'] = [=[10379]=]
, ['price'] = 4917.0
, ['exchange_code'] = [=[]=]
, ['order_num'] = 10256702148
, ['passive_only_order'] = 0
, ['reject_reason'] = [=[]=]
, ['withdraw_datetime'] = { -- table: 00000171D1A2F9A0
 ['min'] = 0
, ['week_day'] = 1
, ['sec'] = 0
, ['ms'] = 0
, ['day'] = 1
, ['year'] = 1601
, ['mcs'] = 0
, ['month'] = 1
, ['hour'] = 0
 }
, ['visible'] = 0.0
, ['yield'] = 0.0
, ['settle_date2'] = 0
, ['price_currency'] = [=[]=]
, ['on_behalf_of_uid'] = 0
, ['awg_price'] = 0.0
, ['linkedorder'] = 0
, ['trans_id'] = 105007541
, ['start_date'] = 0
, ['firmid'] = [=[NC0011100000]=]
, ['value2'] = 0.0
, ['executing_trader_short_code'] = 0
, ['exec_type'] = 0
, ['extref'] = [=[]=]
, ['value'] = 34419.0
, ['repo_value_balance'] = 0.0
, ['repovalue'] = 0.0
, ['datetime'] = { -- table: 00000171D1A2FB20
 ['min'] = 36
, ['week_day'] = 3
, ['sec'] = 57
, ['ms'] = 0
, ['day'] = 18
, ['year'] = 2026
, ['mcs'] = 0
, ['month'] = 2
, ['hour'] = 6
 }
, ['class_code'] = [=[QJSIM]=]
, ['value_entry_type'] = 0
, ['settle_date'] = 0
, ['acnt_type'] = 0
, ['brokerref'] = [=[10379//1]=]
, ['start_discount'] = 0
, ['balance'] = 7.0
, ['side_qualifier'] = 0
, ['seccode'] = [=[YDEX]=]
, ['external_qty'] = 0.0
, ['benchmark'] = [=[]=]
, ['qty2'] = 0.0
, ['client_short_code'] = 0
, ['repoterm'] = 0
, ['price_entry_type'] = 0
, ['visibility_factor'] = 0.0
, ['ext_order_status'] = 0
, ['flags'] = 29
, ['settlecode'] = [=[]=]
, ['uid'] = 228221
, ['expiry'] = -1
, ['bank_acc_id'] = [=[]=]
, ['repo2value'] = 0.0
, ['capacity'] = 0
, ['ordernum'] = 10256702148
, ['price2'] = 0.0
, ['investment_decision_maker_qualifier'] = 0
, ['userid'] = [=[NC0011100000]=]
, ['expiry_time'] = -1
, ['canceled_uid'] = 0
, ['settle_currency'] = [=[]=]
, ['filled_value'] = 0.0
}--таблица: 18.02.26 13:04:52:795
<code>
----
Архив песочницы для анализа поддержкой: https://cloud.mail.ru/public/LTv3/NMDsG3SZD
 
Добрый день, TGB .

В указанный период на сервере проводились технические работы, которые и стали причиной невозможности снятия заявки через Рабочее место и средствами скриптов.
Приносим свои извинения за доставленные неудобства.
 
Цитата
Oleg Kuzembaev написал:
В указанный период на сервере проводились технические работы, которые и стали причиной невозможности снятия заявки
  Здравствуйте! Спасибо за ответ.
  У меня просьба к вам: донести до разработчиков мои комментарии 1, 51.
 
Благодарим вас за обратную связь. Такая информация довольно ценна для улучшения продуктов от нашей компании.

В данном случае, нам видится возможность зарегистрировать пожелание на доработку будущих версий ПО. Однако необходимо достичь понимания, какая именно доработка требуется.
Если у вас уже есть такая наготове, то просьба предметно описать ее: что именно стоит добавить? Как это будет выглядеть в вашем представлении? После сбора всей информации, мы с радостью зарегистрируем такое пожелание.
 
Цитата
Oleg Kuzembaev написал:
В данном случае, нам видится возможность зарегистрировать пожелание на доработку будущих версий ПО. Однако необходимо достичь понимания, какая именно доработка требуется.Если у вас уже есть такая наготове, то просьба предметно описать ее: что именно стоит добавить? Как это будет выглядеть в вашем представлении?

      1. Я исхожу из того, что среди пользователей QUIK могут быть квалифицированные IT-разработчики, которые могут улучшить мои предложения и поэтому выкладываю их в публичку.
      2. Сохранение существующего API QLua, с тем, чтобы обеспечить совместимость с существующими разработками пользователей, является основным ограничением на предлагаемое.
-------------------
I.  В текущей версии QUIK в одном основном потоке обслуживаются:
- запуск всех Lua-скриптов пользователя;
- запуск коллбеков всех Lua-скриптов пользователя;
- обработка всех коллбеков таблиц QUIK (это не таблицы Lua);
- обработка всех индикаторов пользователя.
  Притом, что в текущий момент у подавляющего количества ПК много ядер ЦП, написанное выше явный перебор. Наверное, нет проблем перечисленное выше обрабатывать в отдельных потоках, так как перечисленные выше функции, в моем представлении, не сильно связаны друг с другом и, на первый взгляд, между ними не требуется синхронизация.

II. Интерфейс  взаимодействия QUIK c Lua-скриптом пользователя, реализованный в виде коллбеков, предполагает многопоточный режим использования Lua, порождающий неприятные проблемы параллельного программирования (для решения которых сами же разработчики предлагают использовать потокобезопасную очередь между коллбеками и потоком main). Мне представляется, что имеет смысл вместо коллбеков использовать активную очередь событий, При этом не требуется использовать Lua в многопоточном, редко используемом и не очень стабильном режиме. При этом не будет проблем с подключением новых версий Lua. Более того, скрипты пользователя будут выполняться несколько быстрее из-за отсутствия синхронизации, требуемой в многопоточном варианте использования  Lua.
  Конкретные предложения:
 1) Lua подключать в однопоточном режиме, нативном варианте, без внесения изменений в исходники. Все необходимые коды QLua перенести в dll-пакет.
 2) Для взаимодействия со скриптами пользователя использовать общие для них служебные циклические очереди (в соответствии с существующими функциями обратного вызова). Длины очередей задавать по умолчанию с возможностью их изменения пользователем. Для каждого запускаемого скрипта пользователя создавать блоки доступа (с соответствующими указателями, обеспечивающими чтение очередей без блокировки) к общим циклически очередям.
 3) Функции чтения очередей регистрировать под теми же именами и так же, как это делается для существующих коллбеков.
 4) Выполнять функции чтения очередей в модифицированной следующим образом sleep:
     - sleep выполняется по истечению заданного в ней интервала времени или при получения сигнала поступления данных в одну из циклических очередей (в пропуске сигналов нет проблем, так есть запуск по времени);
     - выполнение функции начинается с анализа появления новых данных в циклических очередях скрипта (конкретного) и запуска зарегистрированных его функций с теми параметрами из очередей, как это делается в существующей версии QUIK;
     - в качестве результата в модифицированной sleep выдавать значения: количество считанных записей в очередях, признак отсутствия потерь данных в очередях из-за их переполнения (с указанием очередей, в которых это возникло), и, возможно, еще что-то.
 Предложенное:
 1) устраняет зависимость служебного потока от пользовательского, когда (служебный) в существующей версии QUIK может быть блокирован пользовательским, а пользовательский служебным;
 2) устраняет необходимость внесения правок в исходники Lua; при этом скрипты пользователя будут выполняться несколько быстрее из-за отсутствия синхронизации, требуемой в многопоточном варианте использования Lua;
 3) обеспечивает контроль потерь данных в очередях, а также возможность исключения этих потерь за счет увеличения длин очередей пользователем или уменьшения времени ожидания в sleep;
 4) убирает проблемы синхронизации внутри скрипта пользователя.

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

IV. Пожелание: сократить время восстановления взаимодействия QUIK с сервером при перезапуске хотя бы до 10 сек.
 
TGB, можете написать прсевдокод, как по-вашему, использовать очередь событий?
 
Цитата
Йцукен написал:
можете написать прсевдокод, как по-вашему, использовать очередь событий?
   Я так понимаю, в случае реализации предложенного.

У пользователя никаких изменений:
Код
  <Определение функций обработки событий ровно так, как это сейчас>

---  Весь код скрипта без изменений, в том числе sleep(<Интервал>) --
-- Отличие только в реализации функции sleep.
-- При  ее вызове с заданным интервалом внутри нее выполняется (код на C++):
    WaitForSingleObject(<ОбъектОжидания>, <Интервал>); // здесь ожидание истечения <Интервала> или выдачи сигнала QUIK (неблокирующей функцией Pulse в служебном потоке) о записи в очереди новых событий.
  -- Когда sleep "срывается" с <ОбъектаОжидания>, то в ней анализируются очереди (это  можно сделать эффективно, используя битовую шкалу непустых очередей скрипта) и выполняются соответствующие <Функций обработки событий> с параметрами считанными из очередей.
  -- Функционально это не отличается от того, что реализовано сейчас, но выполняется в потоке пользователя main.
-- Тому, кому интересны детали выполнения sleep, может изменить в своем скрипте формат вызова sleep: вместо sleep(<Интервал>) строка
     local <Количество обработанных элементов очереди>, <Данные о потерях событий в очередях>  = sleep(<Интервал>) 
 
TGB, колбэки внутри sleep должны вызываться или как?
 
Цитата
Йцукен написал:
колбэки внутри sleep должны вызываться или как?
  Да, так называемые коллбеки, должны вызываться внутри sleep (выполняемой в потоке main) с данными из очередей.
 
TGB, а что делать, если между слипами данные поступили по инструменту несколько раз?
 
Цитата
Йцукен написал:
а что делать, если между слипами данные поступили по инструменту несколько раз?
  При "просыпании" sleep, функции чтения очередей (коллбеки) будут выполняться (последовательно, без пропусков) столько раз сколько необработанных записей в очередях. Если очереди не переполняются, а это можно контролировать и не допускать, то события не теряются, но возможны задержки в их отработке.
  Как было указано ранее, sleep "просыпается" либо по времени, либо по записи в очереди данных из QUIK. Но при этом в в sleep всегда анализируются и при необходимости обрабатываются непустые служебные очереди скрипта.
 
А смысл несколько раз подряд обрабатывать, например OnQuote, если каждый раз мы будем обрабатывать одни и те же данные?
Или вы предлагаете хранить данные для каждого колбэка?
 
Цитата
Йцукен написал:
А смысл несколько раз подряд обрабатывать, например OnQuote, если каждый раз мы будем обрабатывать одни и те же данные?Или вы предлагаете хранить данные для каждого колбэка?
 Если вы не хотите повторно обрабатывать данные, то фильтруйте их. Для фильтрации данных можно создать, для соответствующего вида коллбека, таблицу с фильтруемыми полями, обновляемыми при поступлении новых данных и использовать эту таблицу в начале коллбека с тем. чтобы не обрабатывать ненужные вам повторы.
 
Цитата
TGB написал:
Если вы не хотите повторно обрабатывать данные, то фильтруйте их.
А как вы узнаете, что это повторы, пока не обработаете их?
Цитата
TGB написал:
Для фильтрации данных можно создать, для соответствующего вида коллбека, таблицу с фильтруемыми полями, обновляемыми при поступлении новых данных и использовать эту таблицу в начале коллбека с тем. чтобы не обрабатывать ненужные вам повторы.
Вам надо будет сначала, как минимум запросить данные, чтобы сравнить с предыдущими.
 
Цитата
Йцукен написал:
А как вы узнаете, что это повторы, пока не обработаете их?
   Вы сначала создаете таблицу фильтрации со значениями полей фильтрации заведомо не совпадающими с ожидаемыми данными.
 
Цитата
Йцукен написал:
колбэки внутри sleep должны вызываться или как?
Sleep останавливает поток main,
а функции колбеков вызываются в другом потоке, на который sleep не действует.
 
Цитата
nikolz написал:
Sleep останавливает поток main, а функции колбеков вызываются в другом потоке, на который sleep не действует.
   Вы опять только пишите не читая.
   Мы же с Йцукен обсуждаем не системный Sleep и даже sleep QLua, а предложенный мной запускающий коллбеки.
Вам опять надо проложиться между комментариев :smile: ?
 
Цитата
TGB написал:
Вы опять только пишите не читая.
А зачем, если можно высказать своё "важное" мнение, прочитав только последний комментарий?
 
Цитата
TGB написал:
Цитата
nikolz написал:
Sleep останавливает поток main, а функции колбеков вызываются в другом потоке, на который sleep не действует.
    Вы опять только пишите не читая.
   Мы же с Йцукен обсуждаем не системный Sleep и даже sleep QLua, а предложенный мной запускающий коллбеки.
Вам опять надо проложиться между комментариев :: ?
Что вы так переживаете?
Я написал пояснение о работе sleep в потоках.
Так как г-н Йцукен нефига в этом не понимает, а Вы ему ( я прочитал Ваш ответ)
Цитата
TGB написал:
Цитата
Да, так называемые коллбеки, должны вызываться внутри sleep (выполняемой в потоке main) с данными из очередей.
Это полная чушь.
Ничего внутри sleep не вызывается т к функция sleep выполняется ядром OS.  В это время никакой колбек не успеет ничего сделать.
-------------------
 
 
все что делается внутри sleep - это передается ядру время на которое надо остановить поток. Ядро настраивает таймер на событие -разбудить поток через надцать секунд и передает управление следующему в очереди потоку.
 
Цитата
В любой теме, где есть обсуждение,
Он уже оставил свой след.
Не вникая в суть предложения,
Он вещает свой «важный» бред.
 
Цитата
Йцукен написал:
Цитата
В любой теме, где есть обсуждение,
Он уже оставил свой след.
Не вникая в суть предложения,
Он вещает свой «важный» бред.
Прекрасно, Наконец-то вы занялись самокритикой.
Сами сочинили или опять плагиат?
 
Цитата
nikolz написал:
все что делается внутри sleep - это передается ядру время на которое надо остановить поток.
  Вы, как дятел :smile: , про свое, не относящееся к обсуждаемому.
  Вы это читали ?:
Цитата
TGB написал:
-- Отличие только в реализации функции sleep.
-- При  ее вызове с заданным интервалом внутри нее выполняется (код на C++):
   WaitForSingleObject( ,  ); // здесь ожидание истечения   или выдачи сигнала QUIK (неблокирующей функцией Pulse в служебном потоке) о записи в очереди новых событий.
 -- Когда sleep "срывается" с  , то в ней анализируются очереди (это  можно сделать эффективно, используя битовую шкалу непустых очередей скрипта) и выполняются соответствующие   с параметрами считанными из очередей.
 -- Функционально это не отличается от того, что реализовано сейчас, но выполняется в потоке пользователя main.
 
Цитата
Йцукен написал:
TGB, можете написать прсевдокод, как по-вашему, использовать очередь событий?
Вы не читаете форум.
Я специально выложил для начинающих писателей не псевдокод, а скрипт на Lua с очередью. Да и в документации на QLua есть еще один пример.
--------------------
Ах, ошибся, Вы же не писатель, Вы же ПОЭТ.
 
Цитата
nikolz написал:
Я специально выложил для начинающих писателей не псевдокод, а скрипт на Lua с очередью. Да и в документации на QLua есть еще один пример.
  Какой же вы непонятливый. До сих пор не поняли, что обсуждаются не существующие реализации, а вариант изменения этих реализаций.
  Все таки, похоже, что вы полуинтеллектуальный робот-спамер устаревшей версии, без способности учета контекста обсуждаемой темы при генерации спама.
 
Цитата
TGB написал:
полуинтеллектуальный робот-спамер
Полуробот, скорее.
Цитата
TGB написал:
без способности учета контекста обсуждаемой темы при генерации спама.
У него памяти не хватает, чтобы загрузить весь контекст в память. Вон весь форум загадил кучей новых тем про нехватку памяти.
 
Цитата
TGB написал:
Вы сначала создаете таблицу фильтрации со значениями полей фильтрации заведомо не совпадающими с ожидаемыми данными.
Ну т.е., фактически запросить данные, обработать их (как минимум сравнить с предыдущими).
Логичней было бы, чтобы терминал не дублировал пропущенные колбэки, по которым скрипт не получит новых данных.
Хотя, кому-то наоборот нужна вся история изменений (для индикатора какого-нибудь). Не зря же есть CreateDataSource для параметров инструментов. Но тогда QUIK должен хранить всю историю изменений.
В общем, этот момент вам надо продумать.
 
Цитата
Йцукен написал:
Логичней было бы, чтобы терминал не дублировал пропущенные колбэки, по которым скрипт не получит новых данных.
  Это хорошее ваше предложение можно сформулировать следующим образом:
    функциональность обработки событий QUIK расширить, добавив в API пользователя функцию задания свойств служебных очередей событий:
 1) длин очередей;
 2) функций фильтрации событий перед их записью в соответствующие очереди;
 3) приоритетов обработки очередей;
 4) может быть что то еще..
 Все свойства имеют значение по умолчанию.
 
У меня было предложение, которое стоит повторить:
Цитата
TGB написал:
 На форумах ARQA для комментирующих пользователей ввести месячный лимит трафика, после которого не будет возможность вводить комментарии.   Значением этого лимита могло быть: Годовой трафик nikolz / 12 / 10. Но, конечно, насчет значения лимита, решать ARQA.
 Наличие такого лимита, обеспечило бы:    
  - экономию дискового пространства баз форумов;    
 - автоматическое модерирование форумов за счет принуждения думать о краткости и четкости текстов, пишущих комментаторами;       -     - удобство для читающих комментарии, в которых будет меньше флуда.
  Это может показаться неактуальным из-за снижения активности пишущих, но могло бы улучшить качество форумов в перспективе.
 
Скрытый текст
 
Цитата
TGB написал:
предложение можно сформулировать следующим образом:     функциональность обработки событий QUIK расширить, добавив в API пользователя функцию задания свойств служебных очередей событий
  Оформить это, наверное, лучше в виде меню задания свойств служебных очередей событий QLua, вызываемое в окне запуска скриптов пользователя, при отсутствии запущенных скриптов.
  Промежуточный итог обсуждения интерфейса QUIK со скриптами QLua, при реализации предложений этой ветки следующий:
пользователям, при этом, вносить изменения в их существующие скрипты не требуется.
 
Цитата
Oleg Kuzembaev написал:
необходимо достичь понимания, какая именно доработка требуется.Если у вас уже есть такая наготове, то просьба предметно описать ее: что именно стоит добавить? Как это будет выглядеть в вашем представлении?
Промт для ИИ
 Написать программу на C++, в которой:
   1) Создаются несколько циклических очередей с разными типами данных.
   2) В очереди пишет данные один поток, а читают эти очереди несколько потоков.
   3) У читающих потов должны быть свои указатели чтения циклических очередей.
   4) При записи в любую очередь пишущий поток активирует все читающие потоки.
   5) Каждый читающий поток при чтении получает признак: состояние прочитанных очередей в виде размера
      непрочитанных данных.
--------
Результат от ИИ, полученный в течении 5 сек.(компилируемый, код-основа для разработки очередей событий):
Код
#include <iostream>
#include <vector>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <atomic>

template <typename T, size_t Size>
class CircularQueue {
public:
    void push(T item) {
        buffer[head % Size] = item;
        head.fetch_add(1, std::memory_order_release);
    }

    // Каждый читатель передает свой локальный индекс
    bool try_read(size_t& reader_idx, T& out_item, size_t& pending_count) {
        size_t current_head = head.load(std::memory_order_acquire);
        if (reader_idx < current_head) {
            out_item = buffer[reader_idx % Size];
            reader_idx++;
            pending_count = current_head - reader_idx;
            return true;
        }
        pending_count = 0;
        return false;
    }

private:
    T buffer[Size];
    std::atomic<size_t> head{0};
};

// Контейнер для очередей разных типов
struct SharedData {
    CircularQueue<int, 100> int_queue;
    CircularQueue<double, 100> double_queue;
    std::mutex mtx;
    std::condition_variable cv;
    bool stop = false;
};

void writer(SharedData& data) {
    for (int i = 1; i <= 5; ++i) {
        {
            std::lock_guard<std::mutex> lock(data.mtx);
            data.int_queue.push(i * 10);
            data.double_queue.push(i * 1.1);
            std::cout << "[Writer] Pushed data set " << i << std::endl;
        }
        data.cv.notify_all(); // Активируем всех читателей
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
    data.stop = true;
    data.cv.notify_all();
}

void reader(int id, SharedData& data) {
    size_t my_int_ptr = 0;
    size_t my_dbl_ptr = 0;
    
    while (true) {
        std::unique_lock<std::mutex> lock(data.mtx);
        data.cv.wait(lock, [&] { return data.stop || true; }); // Упрощенно: просыпаемся по сигналу

        int val;
        double dval;
        size_t pending;

        // Читаем из инт-очереди
        while (data.int_queue.try_read(my_int_ptr, val, pending)) {
            std::cout << "Reader " << id << " got INT: " << val << " (Left: " << pending << ")\n";
        }
        // Читаем из дабл-очереди
        while (data.double_queue.try_read(my_dbl_ptr, dval, pending)) {
            std::cout << "Reader " << id << " got DBL: " << dval << " (Left: " << pending << ")\n";
        }

        if (data.stop) break;
    }
}

int main() {
    SharedData data;
    std::thread w(writer, std::ref(data));
    std::thread r1(reader, 1, std::ref(data));
    std::thread r2(reader, 2, std::ref(data));

    w.join();
    r1.join();
    r2.join();
    return 0;
}

// Основные моменты реализации (от ИИ):
// Индивидуальные указатели: Переменные my_int_ptr и my_dbl_ptr находятся в стеке каждого читающего потока. Они отслеживают прогресс конкретного 
// читателя относительно общей «головы» (head) очереди.
// Шаблон очереди: Позволяет создавать экземпляры для int, double или любых других типов в одном контейнере SharedData.
// Синхронизация: std::condition_variable используется для мгновенной активации читателей пишущим потоком.
// Статус очереди: Метод try_read возвращает pending_count, что позволяет читателю знать, сколько данных еще осталось в буфере именно для него.

 
Мне стало интересно, а более продвинутый вариант циклических очередей ИИ сможет реализовать?
                                     Промт для ИИ
 Написать программу на C++, в которой:
   1) Создаются несколько циклических очередей с разными типами данных.
   2) В очереди пишет данные один поток, а читают эти очереди несколько потоков.
   3) У читающих потов должны быть свои указатели чтения циклических очередей и битовые шкалы непустых очередей, хранящиеся в объекте доступа к их очередям.
   4) При записи в любую очередь пишущий поток меняет состояние битовых шкал непустых очередей и активирует все читающие потоки.
   5) Каждый читающий поток при чтении очереди, выбираемой на основании своей битовой шкалы непустых очередей, получает признак: состояние прочитанных очередей в виде размера непрочитанных данных.
   6) Добавить в код динамическое управление количеством очередей и реализовать полностью Lock-Free механизм уведомления без использования std::mutex.

Смог  (все комментарии ИИ):
Код
// Это сложная задача, требующая использования атомарных операций (std::atomic) и барьеров памяти для реализации Lock-Free взаимодействия.
// Для выполнения условия динамического управления очередями и уведомления без мьютексов, мы будем использовать std::atomic_flag или
// std::atomic<bool> для сигнализации (busy-wait или wait/notify в C++20) и атомарные указатели.
// Ниже представлен концептуальный код, реализующий эту логику:
//    C++20   ----
#include <iostream>
#include <vector>
#include <atomic>
#include <thread>
#include <memory>
#include <optional>

// Шаблонная циклическая очередь (Lock-Free для одного писателя и нескольких читателей со своими индексами)
template<typename T, size_t Capacity>
class CircularQueue {
    T buffer[Capacity];
    std::atomic<size_t> head{0}; // Указатель записи

public:
    void push(T item) {
        size_t h = head.load(std::memory_order_relaxed);
        buffer[h % Capacity] = item;
        head.store(h + 1, std::memory_order_release);
    }

    bool try_pop(size_t& reader_idx, T& out_item, size_t& pending_count) {
        size_t current_head = head.load(std::memory_order_acquire);
        if (reader_idx < current_head) {
            out_item = buffer[reader_idx % Capacity];
            reader_idx++;
            pending_count = current_head - reader_idx;
            return true;
        }
        pending_count = 0;
        return false;
    }
};

// Объект доступа для каждого читающего потока
struct ReaderContext {
    std::atomic<uint64_t> bitmask{0}; // Битовая шкала непустых очередей
    std::vector<size_t> read_indices;  // Свои указатели чтения
    std::atomic<bool> data_ready{false};

    void update_mask(size_t queue_idx) {
        bitmask.fetch_or(1ULL << queue_idx, std::memory_order_release);
        data_ready.store(true, std::memory_order_release);
        data_ready.notify_all(); // C++20 Lock-free уведомление
    }
};

class QueueManager {
    CircularQueue<int, 1024> int_queue;
    CircularQueue<float, 1024> float_queue;
    std::vector<std::shared_ptr<ReaderContext>> readers;

public:
    void add_reader(std::shared_ptr<ReaderContext> ctx) {
        ctx->read_indices.resize(2, 0); // 0 для int, 1 для float
        readers.push_back(ctx);
    }

    void write_int(int val) {
        int_queue.push(val);
        for (auto& r : readers) r->update_mask(0);
    }

    void write_float(float val) {
        float_queue.push(val);
        for (auto& r : readers) r->update_mask(1);
    }

    void reader_thread_func(std::shared_ptr<ReaderContext> ctx, int id) {
        while (true) {
            // Ожидание данных (Lock-free wait)
            ctx->data_ready.wait(false, std::memory_order_acquire);
            
            uint64_t mask = ctx->bitmask.load(std::memory_order_acquire);
            for (size_t i = 0; i < 2; ++i) {
                if (mask & (1ULL << i)) {
                    size_t pending = 0;
                    if (i == 0) {
                        int val;
                        while (int_queue.try_pop(ctx->read_indices[0], val, pending)) {
                            printf("[Reader %d] Int: %d (Pending: %zu)\n", id, val, pending);
                        }
                    } else {
                        float val;
                        while (float_queue.try_pop(ctx->read_indices[1], val, pending)) {
                            printf("[Reader %d] Float: %.2f (Pending: %zu)\n", id, val, pending);
                        }
                    }
                    // Сбрасываем бит, если данных больше нет
                    if (pending == 0) ctx->bitmask.fetch_and(~(1ULL << i));
                }
            }
            ctx->data_ready.store(false, std::memory_order_relaxed);
        }
    }
};

int main() {
    QueueManager manager;
    auto r1 = std::make_shared<ReaderContext>();
    auto r2 = std::make_shared<ReaderContext>();

    manager.add_reader(r1);
    manager.add_reader(r2);

    std::thread t1(&QueueManager::reader_thread_func, &manager, r1, 1);
    std::thread t2(&QueueManager::reader_thread_func, &manager, r2, 2);

    // Пишущий поток
    std::thread writer([&]() {
        for (int i = 0; i < 5; ++i) {
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
            manager.write_int(100 + i);
            manager.write_float(1.5f * i);
        }
    });

    writer.join();
    t1.detach(); 
    t2.detach();

    return 0;
}
// Ключевые моменты реализации :
// Индивидуальные индексы : Каждый читатель хранит свои read_indices, что позволяет им двигаться по одной и 
// той же очереди независимо.
// Битовая шкала : Используется std::atomic<uint64_t>, где каждый бит соответствует ID очереди.Это позволяет 
// мгновенно проверить, где появились данные.
// Lock - Free Notify : Использован метод atomic::wait / notify_all(доступен с C++20).Он работает эффективнее 
// обычного yield, так как позволяет потоку "заснуть" без использования мьютекса в пользовательском 
// коде(на уровне ОС это может использовать фьютексы).
// Pending Count : Метод try_pop возвращает разницу между указателем записи и текущим индексом чтения, 
// выполняя условие получения "размера непрочитанных данных".
Страницы: Пред. 1 2
Читают тему
Наверх