<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
	<channel>
		<title>Форум QUIK [тема: Если бы я был архитектором QUIK]</title>
		<link>http://forum.quik.ru</link>
		<description>Новое в теме Если бы я был архитектором QUIK форума  на сайте Форум QUIK [forum.quik.ru]</description>
		<language>ru</language>
		<docs>http://backend.userland.com/rss2</docs>
		<pubDate>Sat, 02 May 2026 16:01:01 +0300</pubDate>
		<item>
			<title>Если бы я был архитектором QUIK</title>
			<description><![CDATA[<b><a href="http://forum.quik.ru/messages/forum10/message81996/topic8563/">Если бы я был архитектором QUIK</a></b> <i> Что стоило бы изменить в QUIK по-крупному</i> в форуме <a href="http://forum.quik.ru/forum10/">Программирование на языке Lua</a>. <br />
			<br />====quote====<br /><a class="blog-p-user-name" id="bp_8eop2hhZ" href="/user/16979/" bx-tooltip-user-id="16979">Oleg Kuzembaev</a> написал:<br />Ваше пожелание зарегистрировано. Мы постараемся рассмотреть его и сообщить Вам результаты анализа. Впоследствии, по результатам анализа, будет приниматься решение о реализации пожелания в будущих версиях ПО.<br />=============<br /> &nbsp; &nbsp;Здравствуйте. &nbsp;Спасибо за ответ.<br />----------------<br /> &nbsp; &nbsp; Вариант реализации очередей с добавлениями.<br />1. &nbsp;//#define win32_API &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // В реализации очередей используется win32_API<br />2. &nbsp;const int QUEUE_RECORDING_MODE = 0; // Режим записи в очереди: 0 - без сигналов (более эффективный чем с сигналами); 1 - с сигналами.<br /> &nbsp; Режим записи в очереди без сигналов добавлен т.к. выдача сигнала &quot;тяжелая&quot; системная функция, в ~100 раз по времени ЦП затратнее, чем одна запись в очередь.<br /> &nbsp; В этом режиме сигналы пишущим потоком не выдаются. <br /> В потоках чтения реализованы два цикла:<br /> &nbsp;1) первый внешний цикл: пользовательский интервал ожидания (аналогичный как в существующем sleep);<br /> &nbsp;2) второй внутри первого, опрашивающий и обрабатывающий очереди событий (функционально аналогичный выполнению существующих коллбеков).<br />3. &nbsp;В тесте очередей учтен случай подключение читающего потока (№ 5) к обработке очередей в произвольный момент времени.<br />
====code====
<pre>
//&nbsp;&nbsp;==============&nbsp;&nbsp;Вариант реализации очередей событий QUIK (Lock-free, ~300 строк) ============
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp; Параметр Q_MAX определяет количество очередей в схеме очередей. Схем обработки очередей можно 
&nbsp;&nbsp;&nbsp;&nbsp;// создать несколько. 
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp; Данные, передаваемые в информационных очередях, строки.
&nbsp;&nbsp;&nbsp;&nbsp;// 1. Формат записи данных - текст описания таблицы Lua: 
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;&nbsp;&nbsp; {name = &#60;Имя коллбека&#62;, tbl = {&#60;Таблица для формирования вызова функции пользователя&#62;}}
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;&nbsp;&nbsp;После чтения выполнять десереализацию текста таблицы Lua и дальше обрабатывать с учетом значения name.
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp; name обеспечивает возможность группировка коллбеков по очередям.
&nbsp;&nbsp;&nbsp;&nbsp;// 2.&nbsp;&nbsp;Коллбеки можно, но необязательно, сгруппировать и группы распределить по очередям с учетом того,
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp; что при чтении обработка начинается с 0-й очереди. Параметры очередей size_max следует задать 
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp; такими, чтобы в них помещались строки их данных.
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;&nbsp;&nbsp; Для коллбеков пользовательских таблиц QUIK, наверное, имеет смысл использовать все таки отдельную 
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp; очередь.
&nbsp;&nbsp;&nbsp;&nbsp;// 3. Параметр FILLING_THRESHOLD - порог заполненности очередей для выдачи предупреждения о высоком уровне 
&nbsp;&nbsp;&nbsp;&nbsp;// заполненности очередей.
&nbsp;&nbsp;&nbsp;&nbsp;// 4. После первого цикла любой очереди, память ее созданных элементов переиспользуется
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;(кроме чтения указателя нет затрат на управление ее памятью).
&nbsp;&nbsp;&nbsp;&nbsp;// 5. Если для читающего потока в какой то его очереди возникнет переполнение, то об этом
&nbsp;&nbsp;&nbsp;&nbsp;// выдается сообщение.
&nbsp;&nbsp;&nbsp;&nbsp;// 6. Реализованы режимы записи в очереди: 0 - без сигналов (более эффективный); 1 - с сигналами.
&nbsp;&nbsp;&nbsp;&nbsp;// ----------------------------------------------------------------------
&nbsp;&nbsp;//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Результат теста (оценка эффективности реализации очередей)
&nbsp;&nbsp;// Режим записи в очереди: без сигналов.
&nbsp;&nbsp;// Писатель выполняет в цикле запись в 10 очередей. Циклов записи : 1000
&nbsp;&nbsp;//&nbsp;&nbsp;T - время выполнения потоков с учетом их пауз в млс.
&nbsp;&nbsp;//--&#62;Писатель. Пауза:&nbsp;&nbsp;1.&nbsp;&nbsp;T (млс.): 1977. Обработано записей: 10000. Время ЦП (млс.): 3.4494
&nbsp;&nbsp;//Читатель 3. Пауза:&nbsp;&nbsp;50.&nbsp;&nbsp;T (млс.): 2023. Обработано записей: 10000. Время ЦП (млс.): 1.3174
&nbsp;&nbsp;//Читатель 1. Пауза:&nbsp;&nbsp; 5.&nbsp;&nbsp;T (млс.): 2023. Обработано записей: 10000. Время ЦП (млс.): 1.1746
&nbsp;&nbsp;//Читатель 4. Пауза: 100.&nbsp;&nbsp;T (млс.): 2023. Обработано записей: 10000. Время ЦП (млс.): 1.2129
&nbsp;&nbsp;//Читатель 2. Пауза:&nbsp;&nbsp;10.&nbsp;&nbsp;T (млс.): 2023. Обработано записей: 10000. Время ЦП (млс.): 1.2194
&nbsp;&nbsp;// -------------------------------------------------------------------------------

//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Краткое ТЗ на разработку очередей 
// 1) Создаются несколько циклических информационных очередей с разными типами данных и общим объектом 
// управления ими.
// 2) В очереди пишет данные один поток, а читают эти очереди несколько потоков.
// 3) Дополнительно создается специальная служебная циклическая очередь: битовые шкалы непустых информационных 
// очередей, записываемых пишущим потоком, после записи в информационные очереди.
// 4) У читающих потоков должны быть свои указатели чтения циклических очередей и их локальные битовые шкалы 
// непустых очередей, хранящиеся в их объекте доступа к информационным очередям.
// 5) Читающие потоки циклически, выполнив свои коды переходят в состояние ожидания сигнала появления данных 
// в их очередях или истечения заданного интервала времени на их общем объекте ожидания.
// 6) При записи в любую очередь пишущий поток после записи в информационные очереди записывает битовые шкалы 
// непустых очередей в служебную очередь шкал непустых очередей и &#91;активирует в режиме 1&#93; все читающие потоки.
// 7) Каждый читающий поток, при пробуждении, читает все появившиеся записи в служебной очереди шкал непустых 
// очередей и формирует свою локальную шкалу непустых очередей и далее, на ее основании, читает без 
// синхронизации, непустые очереди с признаком : состояние прочитанных очередей в виде размера непрочитанных 
// данных.
// 8) Реализовать эффективное управление памятью при передаче данных в этих очередях.

//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; C++20 (используется std::countr_zero) ---
#include &#60;shlobj.h&#62;
#include &#60;windows.h&#62;&#62;
#include &#60;iostream&#62;
#include &#60;vector&#62;
#include &#60;thread&#62;
#include &#60;chrono&#62;
#include &#60;mutex&#62;
&nbsp;&nbsp; //#include &#60;condition_variable&#62;
&nbsp;&nbsp; //#include &#60;atomic&#62;
&nbsp;&nbsp; //#include &#60;bitset&#62;
&nbsp;&nbsp; //#include &#60;intrin.h&#62;
#pragma comment(lib, "winmm.lib")&nbsp;&nbsp;&nbsp;&nbsp;// Установить точность таймера 1 мс

// Параметры тестирования очередей ---
const int N_MAX = 2000;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Количество циклов записи при тестировании
//#define win32_API&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // В реализации очередей используется win32_API
const int QUEUE_RECORDING_MODE = 0; // Режим записи в очереди: 0 - без сигналов; 1 - с сигналами
const INT64 Q_SIZE = 64;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Длина информационной очереди по умолчанию 
const INT64 Q_SIZE_SL = 256;&nbsp;&nbsp;// Длина служебной очереди шкал непустых информационных очередей
const int Q_MAX = 128;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Максимальное количество очередей
const int Количество_очередей = 120; // Количество используемых очередей ( &#60;= Q_MAX)
const int LEN_STR_MAX = 4096; // Размер буфера потоков чтения (максимум size_max_str всех используемых очередей) 
const double FILLING_THRESHOLD = 0.4; // Порог заполненности очередей для выдачи предупреждения об уровне заполненности очередей

// Системное время (синхронизированное). Точность 0,1 мкс.---
static INT64 T_OS_high_mls()
{
&nbsp;&nbsp; FILETIME lpSystemTimeAsFileTime;
&nbsp;&nbsp; GetSystemTimePreciseAsFileTime(&#38;lpSystemTimeAsFileTime);&nbsp;&nbsp;// Системное время (синхронизированное)
&nbsp;&nbsp; INT64 tt = lpSystemTimeAsFileTime.dwHighDateTime;
&nbsp;&nbsp; tt = (tt &#60;&#60; 32) + lpSystemTimeAsFileTime.dwLowDateTime;
&nbsp;&nbsp; return tt;
}

// Строка-объект информационной очереди (переиспользуемая) --
struct q_str {
&nbsp;&nbsp; INT64 tail = 0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Указатель записи ---
&nbsp;&nbsp; INT64 size_max = 0;&nbsp;&nbsp;// Максимальный размер памяти строки (с учетом символа конца строки '&#92;0')
&nbsp;&nbsp; INT64 size = 0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Текущий размер памяти строки (с учетом символа конца строки '&#92;0')
&nbsp;&nbsp; int n_q = 0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Номер очереди&nbsp;&nbsp;
&nbsp;&nbsp; char* str = NULL;&nbsp;&nbsp;&nbsp;&nbsp;// Память строки (размер size_max) ----
&nbsp;&nbsp; int state = 0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Состояние (не используется, но на всякий случай): 1 - занята пишущим потоком; 2 - в очереди.
};

// Параметры очереди, задаваемые при ее инициализации --
struct q_parm {
&nbsp;&nbsp; std::string name_Queue = "";&nbsp;&nbsp;&nbsp;&nbsp; // имя очереди
&nbsp;&nbsp; INT64 q_size = 64;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // длина очереди
&nbsp;&nbsp; INT64 size_max_str = 64;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // максимальная память строки в информационной очереди
};

// Шаблонная циклическая очередь (Lock-free)
// Используется для служебной и информационных очередей&nbsp;&nbsp;
template&#60;typename T&#62;
class InfoQueue {
public:
&nbsp;&nbsp; std::vector&#60;T&#62; buffer;
&nbsp;&nbsp; INT64 tail = 0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // пишет только один писатель
&nbsp;&nbsp; std::string name_Queue = "";&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// имя очереди (можно менять при начальной инициализации)
&nbsp;&nbsp; INT64 q_size = 64;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// размер очереди (можно менять при начальной инициализации)
&nbsp;&nbsp; INT64 size_max_str = 64;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// максимальная память строки в очереди (можно менять при начальной инициализации)
&nbsp;&nbsp; InfoQueue(INT64 s = Q_SIZE) : buffer(s), q_size(s) {}

&nbsp;&nbsp; // Изменение длины очереди ---
&nbsp;&nbsp; void q_size_set(int s) { 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;buffer.reserve(s); q_size = s;
&nbsp;&nbsp; }

&nbsp;&nbsp; // запись в очередь --
&nbsp;&nbsp; bool push(T val) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;buffer&#91;tail++ % q_size&#93; = val;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return true;
&nbsp;&nbsp; }

&nbsp;&nbsp; // Количества доступных элементов очереди для конкретного читателя
&nbsp;&nbsp; // local_head - указатель чтения (читающего потока)
&nbsp;&nbsp; // Если отрицательное значение, то: количество пропущенных при чтении записей очереди --
&nbsp;&nbsp; INT64 get_pending_count(INT64 local_head) const {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;INT64 t = tail - local_head;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return (t &#60;= q_size ? t : q_size - t);
&nbsp;&nbsp; }
};

// Копировать строку-объект в буфер читающего потока для последующей обработки ---
void q_str_copy(q_str* s, q_str* s1, InfoQueue&#60;q_str*&#62;* q)
{
&nbsp;&nbsp; //s1-&#62;size_max = s-&#62;size_max;&nbsp;&nbsp; // ! Задана при создании буфере --
&nbsp;&nbsp; s1-&#62;size = s-&#62;size;
&nbsp;&nbsp; s1-&#62;n_q = s-&#62;n_q;
&nbsp;&nbsp; if (s1-&#62;str == NULL) throw std::runtime_error("*** Ошибка: нет памяти для копирования");
&nbsp;&nbsp; //&nbsp;&nbsp;s1-&#62;size - размер памяти строки (с учетом символа конца строки '&#92;0')
&nbsp;&nbsp; //&nbsp;&nbsp;Длина строки: (s1-&#62;size - 1)&nbsp;&nbsp;
&nbsp;&nbsp; memcpy(s1-&#62;str, s-&#62;str, s1-&#62;size);&nbsp;&nbsp;s1-&#62;tail = s-&#62;tail;&nbsp;&nbsp; // ! Обязательно в таком порядке --
&nbsp;&nbsp; s1-&#62;state = s-&#62;state;
&nbsp;&nbsp; // Дополнительный контроль переполнение очереди (после копирования в буфер потока чтения)
&nbsp;&nbsp; if ((q-&#62;tail - s1-&#62;tail) &#62; q-&#62;q_size) std::cout &#60;&#60; " *** Ошибка переполнение очереди (при копировании) " &#60;&#60; s1-&#62;n_q &#60;&#60; "&#92;n";
}

// Объявления (определяются позже)
struct ReaderAccess;
struct Manager;
//-------------------------------
// Информационные очереди событий --
struct DataCluster {
&nbsp;&nbsp; //&nbsp;&nbsp;Деструктор (! выход из блока кода, в котором определен DataCluster 
&nbsp;&nbsp; // только при завершения потоков, использующих его) --
&nbsp;&nbsp; ~DataCluster() { 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (int i = 0; i &#60; Q_MAX; ++i) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; INT64 size = q&#91;i&#93;.buffer.size();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; INT64 N = (q&#91;i&#93;.tail &#62;= size) ? size : q&#91;i&#93;.tail;&nbsp;&nbsp;// Буфер может быть неполным ---
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int j = 0; j &#60; N; ++j) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;q_str* q_s = q&#91;i&#93;.buffer&#91;1&#93;; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;delete&#91;&#93;q_s-&#62;str;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;delete q_s;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp; }

&nbsp;&nbsp; int count_q = 64;&nbsp;&nbsp;// Количество используемых информационных очередей (&#60;= Q_MAX) --
&nbsp;&nbsp; // При количестве очередей &#62; 64 один бит шкалы непустых очередей соотносится 
&nbsp;&nbsp; // к нескольким ((count_q - 1) / 64 + 1) последовательным очередям, в которых, 
&nbsp;&nbsp; // возможно, есть записи.
&nbsp;&nbsp; UINT32 Queue_grouping = 1; 
&nbsp;&nbsp; InfoQueue&#60;q_str*&#62; q&#91;Q_MAX&#93;;

&nbsp;&nbsp; // Задать количество используемых очередей и их параметры
&nbsp;&nbsp; void set_parm_q(int n, q_parm* parm) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (n &#62; 0 &#38;&#38; n &#60;= Q_MAX)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; count_q = n; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw std::runtime_error(" Количество используемых очередей &#62; Q_MAX или &#60;= 0");&nbsp;&nbsp; /* #### ошибка*/
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Queue_grouping = (count_q - 1) / 64 + 1;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (parm != NULL)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i = 0; i &#60; count_q; ++i) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;q&#91;i&#93;.name_Queue = parm-&#62;name_Queue;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;q&#91;i&#93;.q_size = parm-&#62;q_size;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;q&#91;i&#93;.q_size_set(parm-&#62;q_size);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;q&#91;i&#93;.size_max_str = parm-&#62;size_max_str;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp; }

&nbsp;&nbsp; // Функция формирования маски группировки очередей (многозначное отображение очередей на шкалу непустых)
&nbsp;&nbsp; uint64_t q_mask(int q_i) { return (uint64_t)1 &#60;&#60; (q_i / Queue_grouping); }

&nbsp;&nbsp; //&nbsp;&nbsp;Запрос памяти потоком писателем под запись очереди --
&nbsp;&nbsp; // Запрос памяти системы и формирование строки-объекта -----
&nbsp;&nbsp; q_str* get_q_s(int q_i) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;INT64 size_max_str = q&#91;q_i&#93;.size_max_str;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char* str = new char&#91;size_max_str&#93;;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;str&#91;size_max_str - 1&#93; = '&#92;0';
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;q_str* q_s = new q_str;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;q_s-&#62;str = str;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;q_s-&#62;size_max = size_max_str;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;q_s-&#62;size = 0;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;q_s-&#62;n_q = q_i;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return q_s;
&nbsp;&nbsp; }

&nbsp;&nbsp; // Запрос строки-объекта потоком записи для формирования строки 
&nbsp;&nbsp; q_str* get_str(int q_i) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;q_str* q_s;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;INT64 tail = q&#91;q_i&#93;.tail;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;INT64 t = tail % q&#91;q_i&#93;.q_size;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (q&#91;q_i&#93;.buffer&#91;t&#93; != NULL) {&nbsp;&nbsp; //&nbsp;&nbsp;Получение памяти из очереди --
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; q_s = q&#91;q_i&#93;.buffer&#91;t&#93;; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; q_s-&#62;tail = tail;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else {&nbsp;&nbsp; // Запрос памяти у системы --
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; q_s = get_q_s(q_i);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; q_s-&#62;tail = tail;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return q_s;
&nbsp;&nbsp; }

&nbsp;&nbsp; //&nbsp;&nbsp;Инициализация используемых очередей строками-объектами --
&nbsp;&nbsp; // #### Вряд ли стоит использовать ---
&nbsp;&nbsp; void initialization_q_str() {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (count_q &#62; 0) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i = 0; i &#60; count_q; ++i) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int size = q&#91;i&#93;.q_size;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (int j = 0; j &#60; size; ++j) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (q&#91;i&#93;.buffer&#91;j&#93; == NULL)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;q&#91;i&#93;.buffer&#91;j&#93; = get_q_s(i);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp; }

&nbsp;&nbsp; // Начальная инициализация указателей чтения в потоке-читателе на основе указателей 
&nbsp;&nbsp; // записи в очереди (при начальной инициализации потока)
&nbsp;&nbsp; // Объявление.
&nbsp;&nbsp; void init_access(ReaderAccess* access, Manager* mgr, DataCluster* dc);

&nbsp;&nbsp; // запись в очередь --
&nbsp;&nbsp; bool push(int q_n, q_str* val) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (q_n &#60; 0 || q_n &#62;= count_q) throw std::runtime_error("*** Ошибка: нет такой очереди");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;q&#91;q_n&#93;.buffer&#91;q&#91;q_n&#93;.tail++ % q&#91;q_n&#93;.q_size&#93; = val;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return true;
&nbsp;&nbsp; }
};

//struct ReaderAccess;&nbsp;&nbsp; // Используется в Manager
#ifndef win32_API
// Объект управления потоками чтения 
struct Manager {
&nbsp;&nbsp; INT64 signal_counter = 0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Счетчик сигналов записи
&nbsp;&nbsp; int Выдавать_сигналы = 0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 0 -&nbsp;&nbsp;без выдачи сигнала; 1 - выдача сигнала
&nbsp;&nbsp; InfoQueue&#60;uint64_t&#62; service_q{ Q_SIZE_SL }; // Служебная очередь битовых шкал
&nbsp;&nbsp; //--------- condition_variable --
&nbsp;&nbsp; std::mutex mtx;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Для синхронизации&nbsp;&nbsp;при ожидании&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp; std::condition_variable cv;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Общий объект ожидания 

&nbsp;&nbsp; void pulse(uint64_t mask) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;service_q.push(mask); ++signal_counter;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (Выдавать_сигналы == 1)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cv.notify_all(); 
&nbsp;&nbsp; }

&nbsp;&nbsp; void wait(ReaderAccess* access, int tt);&nbsp;&nbsp;// !! Реализация после struct ReaderAccess (иначе недоступны поля)
};
#else // !win32_API
&nbsp;&nbsp; // Вариант 2. #### Этот вариант работает, но предыдущий лучше --
&nbsp;&nbsp; struct Manager {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;INT64 signal_counter = 0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Счетчик сигналов записи
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int Выдавать_сигналы = 0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 0 -&nbsp;&nbsp;без выдачи сигнала; 1 - выдача сигнала
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;InfoQueue&#60;uint64_t&#62; service_q{ Q_SIZE_SL }; // Служебная очередь битовых шкал
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//--------- condition_variable --
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CRITICAL_SECTION cs;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CONDITION_VARIABLE cv;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Manager() {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; InitializeCriticalSection(&#38;cs); 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; InitializeConditionVariable(&#38;cv);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bool dataReady = false;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;void pulse(uint64_t mask) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; service_q.push(mask);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ++signal_counter;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EnterCriticalSection(&#38;cs);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;#### ?
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (Выдавать_сигналы == 1) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dataReady = true;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WakeAllConditionVariable(&#38;cv); // Будим тех, кто еще спит
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LeaveCriticalSection(&#38;cs);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;#### ?
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;void wait(ReaderAccess* access, int tt);&nbsp;&nbsp;// !! Реализация после struct ReaderAccess (иначе недоступны поля)
&nbsp;&nbsp; };
#endif // !win32_API

// Объект доступа к очередям для читающих потоков --
struct ReaderAccess {
&nbsp;&nbsp; INT64 h&#91;Q_MAX&#93; = { 0 };&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Локальные указатели чтения очередей
&nbsp;&nbsp; INT64 h_service = 0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Указатель чтения служебной очереди
&nbsp;&nbsp; uint64_t local_bitmask = 0;&nbsp;&nbsp;&nbsp;&nbsp;// Локальная шкала непустых очередей потока-читателя
&nbsp;&nbsp; INT64 signal_counter = 0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Локальный счетчик сигналов записи 
&nbsp;&nbsp; //&nbsp;&nbsp;-------- Статистика очередей ---------
&nbsp;&nbsp; INT64 NN&#91;Q_MAX&#93; = { 0 };&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Коичество обращений к непустым очередям
&nbsp;&nbsp; double statistics&#91;Q_MAX&#93; = { 0 }; // Сумма относительной заполненности очередей (среднее = statistics &#91;i&#93;/ (NN&#91;i&#93;))
&nbsp;&nbsp; double q_filling_threshold = 0.4; // Порог заполненности очередей для выдачи предупреждения --
&nbsp;&nbsp; int period_agr = 9;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Период скользящей 
&nbsp;&nbsp; //&nbsp;&nbsp;------- Данные снимка состояния очередей потока чтения ---
&nbsp;&nbsp; int q_len&#91;Q_MAX&#93; = { 0 };&nbsp;&nbsp; // Количество непустых непрочитанных записей в очередях
&nbsp;&nbsp; int q_n&#91;Q_MAX&#93; = { 0 };&nbsp;&nbsp;&nbsp;&nbsp; // Номера непустых очередей
&nbsp;&nbsp; int q_p = 0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;Длина векторов q_n и q_len

&nbsp;&nbsp; // Снимок состояния очередей&nbsp;&nbsp;потока-читателя ("мгновенный", чтобы меньше было повторных чтений очередей).
&nbsp;&nbsp; void snapshot_queue_state(DataCluster&#38; dc, Manager&#38; mgr) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;q_p = 0;&nbsp;&nbsp;// Сброс длины векторов q_n и q_len
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Формирование шкалы непустых очередей потока-читателя и снимка состояния очередей потока-читателя
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;INT64 count = mgr.service_q.get_pending_count(h_service);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (count &#62; 0) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Формируем общую локальную шкалу непустых очередей потока-читателя --
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; local_bitmask = 0;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; uint64_t mask_val;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i = 0; i &#60; count; ++i) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mask_val = mgr.service_q.buffer&#91;h_service % mgr.service_q.q_size&#93;;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;local_bitmask |= mask_val; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;++h_service;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Формирование снимка состояния очередей потока-читателя (на основе его шкалы) --
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int pos_old = 0;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int Queue_grouping = dc.Queue_grouping;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while (local_bitmask) {&nbsp;&nbsp; //&nbsp;&nbsp;Эффективная обработка шкалы непустых очередей --
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;////&nbsp;&nbsp;! Способ определения позиции младшего разряда для старых стандартов (MSVC)&nbsp;&nbsp;-----
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// unsigned long pos3 = 0;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// _BitScanForward64(&#38;pos3, local_bitmask);&nbsp;&nbsp;// позиция младшего разряда с 1
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;////&nbsp;&nbsp;-------------------------------------------------
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int Сдвиг = std::countr_zero(local_bitmask); // C++20 (позиция младшего разряда с 1)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int pos = Сдвиг + pos_old;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pos_old = pos + 1;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pos *= Queue_grouping;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (int i = 0; i &#60; Queue_grouping; ++i) {&nbsp;&nbsp;// обработка групп очередей разряда шкалы --
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; INT64 q_count = dc.q&#91;pos + i&#93;.get_pending_count(h&#91;pos + i&#93;);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (q_count &#62; 0) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int p = q_p;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;q_n&#91;p&#93; = pos + i;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;q_len&#91;p&#93; = q_count;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;Статистика заполненности очереди (относительная, приблизительно средне скользящая) 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (++NN&#91;pos + i&#93; &#60; period_agr) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; statistics&#91;pos + i&#93; += (double)q_count / (double)dc.q&#91;pos + i&#93;.q_size;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; statistics&#91;pos + i&#93; += (double)q_count / (double)dc.q&#91;pos + i&#93;.q_size - statistics&#91;pos + i&#93; / period_agr;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// --------------------------------------------------------------------------------
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;++q_p;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (q_count &#60; 0)&nbsp;&nbsp;std::cout &#60;&#60; " *** Ошибка переполнение очереди (при чтении) q = " &#60;&#60; pos + i &#60;&#60; ": q_count = " &#60;&#60; q_count &#60;&#60; "&#92;n";&nbsp;&nbsp;// #### Ошибка переполнение очереди
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;local_bitmask &#62;&#62;= Сдвиг + 1;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else if (count &#60; 0)&nbsp;&nbsp;std::cout &#60;&#60; " *** -- Ошибка переполнение служебной очереди шкал --&#92;n";
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//std::cout &#60;&#60; ".&nbsp;&nbsp;Шкала : " &#60;&#60; std::bitset&#60;64&#62;(local_bitmask) &#60;&#60; "&#92;n";
&nbsp;&nbsp; }

&nbsp;&nbsp; // Чтение строки-объекта очереди --
&nbsp;&nbsp; // q_n - номер очереди
&nbsp;&nbsp; q_str* get_q_str(int q_n, DataCluster&#38; dc) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;q_str* val = dc.q&#91;q_n&#93;.buffer&#91;h&#91;q_n&#93; % dc.q&#91;q_n&#93;.q_size&#93;;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;++h&#91;q_n&#93;;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return val;
&nbsp;&nbsp; }
};

// ! Реализация члена DataCluster (расположена после struct ReaderAccess, Manager и DataCluster 
// чтобы в методе были доступны их поля)
// Начальная инициализация указателей чтения в потоке-читателе на основе указателей 
// записи в очереди (при начальной инициализации потока)
void DataCluster::init_access(ReaderAccess* access, Manager* mgr, DataCluster* dc) {
&nbsp;&nbsp; int count_q = dc-&#62;count_q;
&nbsp;&nbsp; if (count_q &#60;= 0)&nbsp;&nbsp;throw std::runtime_error("*** Ошибка: не задано количество используемых очередей");
&nbsp;&nbsp; // Получение указателя служебной очереди
&nbsp;&nbsp; access-&#62;h_service = mgr-&#62;service_q.tail;
&nbsp;&nbsp; // Получение указателей информационных очередей 
&nbsp;&nbsp; for (int i = 0; i &#60; count_q; ++i) access-&#62;h&#91;i&#93; = q&#91;i&#93;.tail;
}

// ! Реализация члена Manager (расположена после struct ReaderAccess чтобы в методе были 
// доступны ее поля)
// Пауза
#ifndef win32_API
void Manager::wait(ReaderAccess* access, int dt) {
&nbsp;&nbsp; if (access-&#62;signal_counter == signal_counter) {// не было сигнала записи при отсутствии ожидания в потоке чтения
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;std::unique_lock&#60;std::mutex&#62; lock(mtx); 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cv.wait_for(lock, std::chrono::milliseconds(2)) == std::cv_status::timeout;
&nbsp;&nbsp; }
&nbsp;&nbsp; access-&#62;signal_counter = signal_counter;&nbsp;&nbsp; // Обрабатываемый сигнал записи в потоке чтения
}
#else&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // !win32_API
&nbsp;&nbsp; // Вариант 2. #### -------
&nbsp;&nbsp; void Manager::wait(ReaderAccess* access, int dt) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (access-&#62;signal_counter == signal_counter) {// не было сигнала записи при отсутствии ожидания в потоке чтения
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EnterCriticalSection(&#38;cs);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Пытаемся дождаться условия в течение заданного timeout
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (!dataReady) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!SleepConditionVariableCS(&#38;cv, &#38;cs, 2)) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (GetLastError() == ERROR_TIMEOUT) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //std::cout &#60;&#60; "Thread &#91;" &#60;&#60; tt &#60;&#60; "ms&#93; TIMED OUT!" &#60;&#60; std::endl;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//std::cout &#60;&#60; "Thread &#91;" &#60;&#60; tt &#60;&#60; "ms&#93; GOT SIGNAL!" &#60;&#60; std::endl;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dataReady = false;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LeaveCriticalSection(&#38;cs);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;access-&#62;signal_counter = signal_counter;&nbsp;&nbsp; // Обрабатываемый сигнал записи в потоке чтения
&nbsp;&nbsp; }
#endif // !win32_API

//&nbsp;&nbsp;Тестирование очередей событий&nbsp;&nbsp;------------------------------------------------
// Формат записываемых строк общий для всех очередей: тексты таблиц Lua (сереализация).
// Читающий поток выполняет десереализацию полученных строк и запуск Lua-функций обработки событий.
//&nbsp;&nbsp;&nbsp;&nbsp;----------------------------------------------------
//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Функция потока-писателя
void producer(DataCluster&#38; dc, Manager&#38; mgr) {
&nbsp;&nbsp; std::cout &#60;&#60; "Писатель. Запись выполняется в 10 очередей. Циклов записи: " &#60;&#60; N_MAX &#60;&#60; std::endl;
&nbsp;&nbsp; std::cout &#60;&#60; "T - время выполнения потоков с учетом их пауз." &#60;&#60; std::endl;
&nbsp;&nbsp; // -------------------------------------
&nbsp;&nbsp; INT64 tt = T_OS_high_mls();&nbsp;&nbsp;// Для подсчета времени ЦП --
&nbsp;&nbsp; INT64 TT = tt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Для замера времени выполнения&nbsp;&nbsp;---
&nbsp;&nbsp; INT64 TCP = 0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Время ЦП&nbsp;&nbsp;---
&nbsp;&nbsp; INT64 NRQ = 0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Количество обработанных записей
&nbsp;&nbsp; int dt = 1;
&nbsp;&nbsp; int q_n_test = 0;
&nbsp;&nbsp; for (int i = 1; i &#60;= N_MAX; ++i) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tt = T_OS_high_mls() - tt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TCP += tt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//std::this_thread::sleep_for(std::chrono::milliseconds(dt));&nbsp;&nbsp; // Пауза потока записи
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Sleep(dt);&nbsp;&nbsp; // И так можно 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tt = T_OS_high_mls();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;%%%%%% Подготовка данных для записи в очереди в виде C-строк

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp; Запись строк в очереди --
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;uint64_t mask = 0;&nbsp;&nbsp;// шкала непустых очередей
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Запись в разные очереди
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// !!&nbsp;&nbsp;rec-&#62;size - размер памяти строки (с учетом символа конца строки '&#92;0')
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;&nbsp;&nbsp; Длина строки: (rec-&#62;size - 1) 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const char* source = "{name = 'OnCleanUp',tbl = {&nbsp;&nbsp;....&#93; }"; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int sz = strlen(source) + 1;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;q_n_test %= 2; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int q_n_test1 = q_n_test * 4;&nbsp;&nbsp; // Для записи в разные очереди 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;q_str* rec = dc.get_str(0 + q_n_test1); memcpy(rec-&#62;str, source, sz); rec-&#62;size = sz;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (dc.push(0 + q_n_test1, rec))&nbsp;&nbsp;mask |= dc.q_mask(0 + q_n_test1);&nbsp;&nbsp;++NRQ;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rec = dc.get_str(1 + q_n_test1);&nbsp;&nbsp;memcpy(rec-&#62;str, source, sz); rec-&#62;size = sz;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (dc.push(1 + q_n_test1, rec))&nbsp;&nbsp;mask |= dc.q_mask(1 + q_n_test1);&nbsp;&nbsp;++NRQ;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rec = dc.get_str(2 + q_n_test1);&nbsp;&nbsp;memcpy(rec-&#62;str, source, sz); rec-&#62;size = sz;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (dc.push(2 + q_n_test1, rec))&nbsp;&nbsp;mask |= dc.q_mask(2 + q_n_test1);&nbsp;&nbsp;++NRQ;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rec = dc.get_str(3);&nbsp;&nbsp;memcpy(rec-&#62;str, source, sz); rec-&#62;size = sz;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (dc.push(3 + q_n_test1, rec))&nbsp;&nbsp;mask |= dc.q_mask(3 + q_n_test1);&nbsp;&nbsp;++NRQ;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;++q_n_test;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rec = dc.get_str(40);&nbsp;&nbsp;memcpy(rec-&#62;str, source, sz); rec-&#62;size = sz;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (dc.push(40, rec))&nbsp;&nbsp;mask |= dc.q_mask(40);&nbsp;&nbsp;++NRQ;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rec = dc.get_str(41);&nbsp;&nbsp;memcpy(rec-&#62;str, source, sz); rec-&#62;size = sz;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (dc.push(41, rec))&nbsp;&nbsp;mask |= dc.q_mask(41);&nbsp;&nbsp;++NRQ;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rec = dc.get_str(40);&nbsp;&nbsp;memcpy(rec-&#62;str, source, sz); rec-&#62;size = sz;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (dc.push(40, rec))&nbsp;&nbsp;mask |= dc.q_mask(40);&nbsp;&nbsp;++NRQ;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rec = dc.get_str(41);&nbsp;&nbsp;memcpy(rec-&#62;str, source, sz); rec-&#62;size = sz;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (dc.push(41, rec))&nbsp;&nbsp;mask |= dc.q_mask(41);&nbsp;&nbsp;++NRQ;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rec = dc.get_str(42);&nbsp;&nbsp;memcpy(rec-&#62;str, source, sz); rec-&#62;size = sz;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (dc.push(42, rec))&nbsp;&nbsp;mask |= dc.q_mask(42);&nbsp;&nbsp;++NRQ;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rec = dc.get_str(100); memcpy(rec-&#62;str, "шшшшшш", 7); rec-&#62;size = 7;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (i &#62;= N_MAX) rec-&#62;state= -1;&nbsp;&nbsp; // Признак читателям завершить поток ---
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (dc.push(100, rec)) mask |= dc.q_mask(100);&nbsp;&nbsp;++NRQ;&nbsp;&nbsp; // "шшшшшш"
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// std::cout &#60;&#60; "&#91;P&#93; Данные записаны, маска: " &#60;&#60; std::bitset&#60;64&#62;(mask) &#60;&#60; std::endl;&nbsp;&nbsp;// ####
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Запись маски и активация потоков
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mgr.pulse(mask);
&nbsp;&nbsp; }
&nbsp;&nbsp; std::stringstream out;&nbsp;&nbsp; // можно использовать &#60;&#60;
&nbsp;&nbsp; out &#60;&#60; "---&#62;&nbsp;&nbsp; Писатель. Пауза:" &#60;&#60; dt
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#60;&#60; ". T (млс.):"
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#60;&#60; (int)((T_OS_high_mls() - TT) * 0.0001) &#60;&#60; ". Обработано записей:" &#60;&#60; NRQ
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#60;&#60; ". Время ЦП (млс.):" &#60;&#60; TCP * 0.0001 &#60;&#60; "&#92;n";
&nbsp;&nbsp; std::string result = out.str();&nbsp;&nbsp;&nbsp;&nbsp;// Получаем std::string
&nbsp;&nbsp; std::cout &#60;&#60; result;
}

// Заглушка функции обработки событий --
//&nbsp;&nbsp; Выполняет десереализацию полученных строк и запуск Lua-функций обработки событий
// в зависимости от name (вида очереди)
//&nbsp;&nbsp;1) !! Память q_s возвращать не надо
//&nbsp;&nbsp;2) !! q_s-&#62;size - размер памяти строки (с учетом символа конца строки '&#92;0')
//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Длина строки: (q_s-&#62;size - 1) 
void event_handling(q_str* q_s, std::string name) {

}

//&nbsp;&nbsp;Функция потоков-читателей --
//&nbsp;&nbsp;filling_threshold - порог заполненности очередей (&#60;текущее количество записей очереди&#62; / &#60;размер очереди&#62;) 
//&nbsp;&nbsp;для выдачи предупреждений: "Превышен порог заполненности очередей" 
//&nbsp;&nbsp;dt - пауза (млс.)&nbsp;&nbsp; 
void consumer(int id, DataCluster&#38; dc, Manager&#38; mgr, double filling_threshold, int dt) {
&nbsp;&nbsp; ReaderAccess access;
&nbsp;&nbsp; access.q_filling_threshold = filling_threshold;
&nbsp;&nbsp; // Инициализация access.h и access-&#62;h_service с учетом текущего состояния очередей
&nbsp;&nbsp; dc.init_access(&#38;access, &#38;mgr, &#38;dc);&nbsp;&nbsp;// "Встраивание" потока чтения в очереди
&nbsp;&nbsp; // Буфер строки-объекта для дальнейшей обработки - q_str_c --
&nbsp;&nbsp; const char str_copy&#91;LEN_STR_MAX&#93; = {'&#92;0'};
&nbsp;&nbsp; q_str q_str_c;&nbsp;&nbsp;q_str_c.str = (char*)str_copy; q_str_c.size_max = LEN_STR_MAX;
&nbsp;&nbsp; // -------------------------------------
&nbsp;&nbsp; INT64 tt = T_OS_high_mls();
&nbsp;&nbsp; INT64 TT = tt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Для замера времени выполнения&nbsp;&nbsp;---
&nbsp;&nbsp; dt = dt &#62; 1 ? dt : 1;
&nbsp;&nbsp; INT64 DT = dt * 10000;
&nbsp;&nbsp; INT64 DTT = 0;
&nbsp;&nbsp; INT64 TCP = 0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Для замера времени ЦП обработки очередей
&nbsp;&nbsp; INT64 NRQ = 0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Количество обработанных записей
&nbsp;&nbsp; INT64 NACT = 0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Количество активаций потока чтения

&nbsp;&nbsp; while (true) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Пользовательский цикл обработки 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tt = T_OS_high_mls();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DTT = T_OS_high_mls() + DT;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;while ((T_OS_high_mls() - DTT) &#60; 0) {&nbsp;&nbsp;// Цикл обработки очерелей событий
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tt = (T_OS_high_mls() - tt);&nbsp;&nbsp;// время обработки потока в цикле --
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TCP += tt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Пауза 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mgr.wait(&#38;access, dt);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tt = T_OS_high_mls();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ++NACT;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;&nbsp;------------------------------------------------------------------
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;&nbsp; Получение снимка состояния очередей потока
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; access.snapshot_queue_state(dc, mgr);

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;&nbsp;Обработка снимка состояния очередей потока: 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;&nbsp;1) access.q_p - вектор номеров непустых очередей 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;&nbsp;2) access.q_len - вектор длин необработанных очередей
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;&nbsp;3) access.q_p - длина векторов 1), 2)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int q_p = access.q_p;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Просмотр и обработка вектора непустых очередей потока чтения
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i = 0; i &#60; q_p; ++i) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int q_n = access.q_n&#91;i&#93;;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int count = access.q_len&#91;i&#93;;&nbsp;&nbsp;// Количество записей в очереди --
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp; ------- Сбор статистики ----
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;double statistics_agr = access.statistics&#91;q_n&#93; / (access.NN&#91;q_n&#93; &#60; access.period_agr ? access.NN&#91;q_n&#93; : access.period_agr);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (statistics_agr &#62; access.q_filling_threshold)&nbsp;&nbsp;//&nbsp;&nbsp;#### Предупреждающее сообщение --
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; std::cout &#60;&#60; "&#91;Поток: " &#60;&#60; id &#60;&#60; "&#93; Очередь "
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#60;&#60; q_n &#60;&#60; ".&nbsp;&nbsp;Заполненность (средняя): " &#60;&#60; statistics_agr
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#60;&#60; ".&nbsp;&nbsp;*** Превышен порог заполненности очередей: " &#60;&#60; access.q_filling_threshold &#60;&#60; "&#92;n";

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;////&nbsp;&nbsp; Отладочная печать 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//if (count &#62; 0 || count &#60; 0)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp; std::cout &#60;&#60; "&#91;Поток: " &#60;&#60; id &#60;&#60; "&#93; Очередь "
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp; &#60;&#60; q_n &#60;&#60; ".&nbsp;&nbsp;Заполненность (средняя): " &#60;&#60; statistics_agr
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp; &#60;&#60; ".&nbsp;&nbsp;Количество: " &#60;&#60; count &#60;&#60; ". Ук чтения " &#60;&#60; access.h&#91;q_n&#93; &#60;&#60; "&#92;n";
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;--------------------
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;INT64 q_size = dc.q&#91;q_n&#93;.q_size;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Чтение и обработка очередей 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;while (count--) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; q_str* val_q = access.get_q_str(q_n, dc);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;&nbsp; Копировать запись очереди в буфер обработки q_str_c (типа q_str), 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // созданный в стеке --
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; q_str_copy(val_q, &#38;q_str_c, &#38;dc.q&#91;q_n&#93;);&nbsp;&nbsp;val_q = NULL;&nbsp;&nbsp; // ! далее использовать только q_str_c
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; event_handling(&#38;q_str_c, dc.q&#91;q_n&#93;.name_Queue);&nbsp;&nbsp; //&nbsp;&nbsp;Вызов функции обработки записи очереди
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ++NRQ;&nbsp;&nbsp; // Количество обработанных записей 

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Результат теста 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Завершение потока при тестировании --
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (q_str_c.state == -1) {&nbsp;&nbsp; // Признак читателям завершить поток ---
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;std::stringstream out;&nbsp;&nbsp;&nbsp;&nbsp;// можно использовать &#60;&#60;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;out &#60;&#60; "Читатель " &#60;&#60; id &#60;&#60; ". Пауза:" &#60;&#60; dt
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#60;&#60; ". T (млс.):"
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#60;&#60; (int)((T_OS_high_mls() - TT) * 0.0001) &#60;&#60; ". Обработано записей:" &#60;&#60; NRQ
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#60;&#60; ". Циклов обработки:" &#60;&#60; NACT
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#60;&#60; ". Время ЦП (млс.):" &#60;&#60; TCP * 0.0001 &#60;&#60; "&#92;n";
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;std::string result = out.str();&nbsp;&nbsp;&nbsp;&nbsp;// Получаем std::string
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;std::cout &#60;&#60; result;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;&nbsp;Отладочная печать 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //const char* val_str = q_str_c.str; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //if (access.h&#91;q_n&#93; == N_MAX) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;&nbsp; std::cout &#60;&#60; " Поток " &#60;&#60; id &#60;&#60; ". Очередь " &#60;&#60; q_n &#60;&#60; " -&#62; Val(str): " &#60;&#60; val_str &#60;&#60; ". Указатель чтения "
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#60;&#60; access.h&#91;q_n&#93; &#60;&#60; ". Интервал (млс.) " &#60;&#60; (T_OS_high_mls() - TT) * 0.0001 &#60;&#60; "&#92;n";
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//// %%%%%%%%%%%% Обработка остальных кодов (пользователя) в потоке чтения -- 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//if (dt &#62; 50)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp; std::cout &#60;&#60; " Поток " &#60;&#60; "&#92;n";
&nbsp;&nbsp; }
}

//&nbsp;&nbsp; ---------------------&nbsp;&nbsp;Тест очередей&nbsp;&nbsp;-------------------------
int main() {
&nbsp;&nbsp; SetConsoleCP(1251);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Кодировка 1251 (! #include &#60;shlobj.h&#62;)
&nbsp;&nbsp; setlocale(LC_ALL, "");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // #### Русификация вывода ввода (работает) -----
&nbsp;&nbsp; timeBeginPeriod(1); // Установить точность 1 мс
&nbsp;&nbsp; // ==================================
#ifdef win32_API
&nbsp;&nbsp; std::cout &#60;&#60; "В реализации используется win32_API" &#60;&#60; std::endl;
#endif
&nbsp;&nbsp; DataCluster dc;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Очереди ---
&nbsp;&nbsp; //&nbsp;&nbsp;1. Для каждой очереди должен быть задан размер памяти для максимальной строки, передаваемой 
&nbsp;&nbsp; //&nbsp;&nbsp;&nbsp;&nbsp; в ней (с учетом признака конца строки '&#92;0'). 
&nbsp;&nbsp; //&nbsp;&nbsp;2. В вызове метода set_parm_q второй параметр: вектор типа q_parm * с параметрами очередей
&nbsp;&nbsp; //&nbsp;&nbsp;3. Порядок элементов в векторе определяет приоритет обработки очередей (0 -&#62; 1 ...)
&nbsp;&nbsp; q_parm* Параметры_очередей = NULL;&nbsp;&nbsp; // Параметры_очередей&#91;Количество_очередей&#93; 
&nbsp;&nbsp; dc.set_parm_q(Количество_очередей, Параметры_очередей);
&nbsp;&nbsp; //dc.initialization_q_str();&nbsp;&nbsp;&nbsp;&nbsp;// #### Вряд ли стоит использовать ---
&nbsp;&nbsp; Manager mgr;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Объект управления потоками чтения 
&nbsp;&nbsp; mgr.Выдавать_сигналы = QUEUE_RECORDING_MODE; // Режим записи в очереди: 0 - без сигналов;&nbsp;&nbsp;1 - с сигналами
&nbsp;&nbsp; std::cout &#60;&#60; "Режим записи в очереди: " &#60;&#60; (mgr.Выдавать_сигналы == 0 ? "без сигналов" : "с сигналами") &#60;&#60; std::endl;

&nbsp;&nbsp; //&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Читающие потоки --
&nbsp;&nbsp; // Последний параметр интервал ожидания (млс.)
&nbsp;&nbsp; std::thread c1(consumer, 1, std::ref(dc), std::ref(mgr), FILLING_THRESHOLD, 5);
&nbsp;&nbsp; std::thread c2(consumer, 2, std::ref(dc), std::ref(mgr), FILLING_THRESHOLD, 10);
&nbsp;&nbsp; std::thread c3(consumer, 3, std::ref(dc), std::ref(mgr), FILLING_THRESHOLD, 50);
&nbsp;&nbsp; std::thread c4(consumer, 4, std::ref(dc), std::ref(mgr), FILLING_THRESHOLD, 100);
&nbsp;&nbsp; //&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Пишущий поток --
&nbsp;&nbsp; std::thread p(producer, std::ref(dc), std::ref(mgr));

&nbsp;&nbsp; //&nbsp;&nbsp;подключение читающего потока к обработке очередей в произвольный момент времени. 
&nbsp;&nbsp; const int Задержка_запуска_читателя = 700;
&nbsp;&nbsp; std::cout &#60;&#60; "Задержка_запуска_читателя 5 на " &#60;&#60; Задержка_запуска_читателя &#60;&#60; " млс." &#60;&#60; std::endl;
&nbsp;&nbsp; Sleep(Задержка_запуска_читателя);
&nbsp;&nbsp; std::thread c5(consumer, 5, std::ref(dc), std::ref(mgr), FILLING_THRESHOLD, 1);

&nbsp;&nbsp; p.join(); c1.join(); c2.join(); c3.join(); c4.join(); c5.join();
&nbsp;&nbsp; timeEndPeriod(1); // Освободить точность 1 млс.
&nbsp;&nbsp; char nazv_sh&#91;100&#93;;
&nbsp;&nbsp; std::cout &#60;&#60; "Введите что-нибудь:" &#60;&#60; std::endl;
&nbsp;&nbsp; std::cin &#62;&#62; nazv_sh;
&nbsp;&nbsp; return 0;
}
</pre>
============= <br />
			<i>14.03.2026 08:31:26, TGB.</i>]]></description>
			<link>http://forum.quik.ru/messages/forum10/message81996/topic8563/</link>
			<guid>http://forum.quik.ru/messages/forum10/message81996/topic8563/</guid>
			<pubDate>Sat, 14 Mar 2026 08:31:26 +0300</pubDate>
			<category>Программирование на языке Lua</category>
		</item>
		<item>
			<title>Если бы я был архитектором QUIK</title>
			<description><![CDATA[<b><a href="http://forum.quik.ru/messages/forum10/message81986/topic8563/">Если бы я был архитектором QUIK</a></b> <i> Что стоило бы изменить в QUIK по-крупному</i> в форуме <a href="http://forum.quik.ru/forum10/">Программирование на языке Lua</a>. <br />
			<a class="blog-p-user-name" id="bp_fzlCPk58" href="/user/13952/" bx-tooltip-user-id="13952">TGB</a>, здравствуйте.<br /><br />Ваше пожелание зарегистрировано. Мы постараемся рассмотреть его и сообщить Вам результаты анализа. Впоследствии, по результатам анализа, будет приниматься решение о реализации пожелания в будущих версиях ПО. <br />
			<i>13.03.2026 11:50:23, Oleg Kuzembaev.</i>]]></description>
			<link>http://forum.quik.ru/messages/forum10/message81986/topic8563/</link>
			<guid>http://forum.quik.ru/messages/forum10/message81986/topic8563/</guid>
			<pubDate>Fri, 13 Mar 2026 11:50:23 +0300</pubDate>
			<category>Программирование на языке Lua</category>
		</item>
		<item>
			<title>Если бы я был архитектором QUIK</title>
			<description><![CDATA[<b><a href="http://forum.quik.ru/messages/forum10/message81937/topic8563/">Если бы я был архитектором QUIK</a></b> <i> Что стоило бы изменить в QUIK по-крупному</i> в форуме <a href="http://forum.quik.ru/forum10/">Программирование на языке Lua</a>. <br />
			В классе InfoQueue, в методе <br /><br />====quote====<br /><a class="blog-p-user-name" id="bp_DMkNvi7R" href="/user/13952/" bx-tooltip-user-id="13952">TGB</a> написал:<br /> &nbsp; // запись в очередь --<br /> &nbsp; bool push(T val) {<br /> &nbsp; &nbsp; &nbsp;if (tail &lt; q_size)<br /> &nbsp; &nbsp; &nbsp; &nbsp; buffer[tail % q_size] = val;<br /> &nbsp; &nbsp; &nbsp;++tail;<br /> &nbsp; &nbsp; &nbsp;return true;<br /> &nbsp; }<br />=============<br /> &nbsp;Ошибка: строка с if лишняя.<br />Правильно:<br />
====code====
<pre>
 // запись в очередь --
&nbsp;&nbsp; bool push(T val) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; buffer&#91;tail % q_size&#93; = val;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;++tail;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return true;
&nbsp;&nbsp;}
</pre>
============= <br />
			<i>11.03.2026 08:57:19, TGB.</i>]]></description>
			<link>http://forum.quik.ru/messages/forum10/message81937/topic8563/</link>
			<guid>http://forum.quik.ru/messages/forum10/message81937/topic8563/</guid>
			<pubDate>Wed, 11 Mar 2026 08:57:19 +0300</pubDate>
			<category>Программирование на языке Lua</category>
		</item>
		<item>
			<title>Если бы я был архитектором QUIK</title>
			<description><![CDATA[<b><a href="http://forum.quik.ru/messages/forum10/message81923/topic8563/">Если бы я был архитектором QUIK</a></b> <i> Что стоило бы изменить в QUIK по-крупному</i> в форуме <a href="http://forum.quik.ru/forum10/">Программирование на языке Lua</a>. <br />
			В соответствии с ТЗ (п.1,2, 4-8) ниже выложен код на C++ варианта реализации (Lock-free) циклических очередей событий QUIK.<br /> &nbsp; &nbsp; &nbsp;При использовании этого варианта в реализации п. 9-11 необходимости нет.<br /> &nbsp; 1. Блокировок между пишущим основным потоком QUIK и читающими потоками (main) нет.<br /> &nbsp; 2. Результата выполненных тестов эффективности приведены в комментариях кодов. <br /> &nbsp; &nbsp; &nbsp;В тестах накладные расходы времени ЦП: <br /> &nbsp; &nbsp; &nbsp; 1) на запись в очередь одного сообщения пишущим потоком ~0.007 мкс.<br /> &nbsp; &nbsp; &nbsp; 2) на чтение одного сообщения читающим потоком ~0.13 мкс.<br /> &nbsp; 3. Порог заполненности очередей обеспечивает выдачу предупреждения о высоком уровне заполненности очередей для читающих потоков.<br /> &nbsp; &nbsp; &nbsp;Если для читающего потока в какой то его очереди возникнет переполнение, то об этом выдается сообщение.<br /> &nbsp; 4. Независимо от задаваемой паузы ожидания в читающем потоке, он активируется всякий раз, при появлении новых записей в его очередях.<br /> &nbsp; &nbsp; &nbsp;Вычисляемая (реальная) пауза читающего потока = 'пауза ожидания' - 'время обработки его цикла (новых коллбеков и кодов пользователя)', но &gt;= 1 млс.<br /> &nbsp; &nbsp; &nbsp;В выложенном коде устанавливается разрешение таймера = 1 млс. (вместо 16,...).<br /> &nbsp; 5. Все основные характеристики очередей параметризированы. &nbsp;Функция запроса свойств очередей в выложенном коде не реализована из-за ее простоты. &nbsp;<br /> &nbsp; 6. Выложенный вариант обеспечивает, при его использовании, сохранения существующего API QLua.<br />
====code====
<pre>
//&nbsp;&nbsp;==============&nbsp;&nbsp;Вариант реализации очередей событий QUIK (Lock-free, ~300 строк) ============
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp; Параметр Q_MAX определяет количество очередей в схеме очередей. Схем обработки очередей можно 
&nbsp;&nbsp;&nbsp;&nbsp;// создать несколько. 
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp; Данные, передаваемые в информационных очередях, строки.
&nbsp;&nbsp;&nbsp;&nbsp;// 1. Формат записи данных - текст описания таблицы Lua: 
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;&nbsp;&nbsp; {name = &#60;Имя коллбека&#62;, tbl = {&#60;Таблица для формирования вызова функции пользователя&#62;}}
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;&nbsp;&nbsp;После чтения выполнять десереализацию текста таблицы Lua и дальше обрабатывать с учетом значения name.
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp; name обеспечивает возможность группировка коллбеков по очередям.
&nbsp;&nbsp;&nbsp;&nbsp;// 2.&nbsp;&nbsp;Коллбеки можно, но необязательно, сгруппировать и группы распределить по очередям с учетом того,
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp; что при чтении обработка начинается с 0-й очереди. Параметры очередей size_max следует задать 
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp; такими, чтобы в них помещались строки их данных.
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;&nbsp;&nbsp; Для коллбеков пользовательских таблиц QUIK, наверное, имеет смысл использовать все таки отдельную 
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp; очередь.
&nbsp;&nbsp;&nbsp;&nbsp;// 3. Параметр FILLING_THRESHOLD - порог заполненности очередей для выдачи предупреждения о высоком уровне 
&nbsp;&nbsp;&nbsp;&nbsp;// заполненности очередей.
&nbsp;&nbsp;&nbsp;&nbsp;// 4. После первого цикла любой очереди, память ее созданных элементов переиспользуется
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;(кроме чтения указателя нет затрат на управление ее памятью).
&nbsp;&nbsp;&nbsp;&nbsp;// 5. Если для читающего потока в какой то его очереди возникнет переполнение, то об этом
&nbsp;&nbsp;&nbsp;&nbsp;// выдается сообщение.
&nbsp;&nbsp;&nbsp;&nbsp;// ----------------------------------------------------------------------
&nbsp;&nbsp;//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Результат теста (оценка эффективности реализации очередей)
&nbsp;&nbsp;// Писатель выполняет в цикле запись в 10 очередей. Циклов записи : 1000
&nbsp;&nbsp;//&nbsp;&nbsp;T - время выполнения потоков с учетом их пауз в млс.
&nbsp;&nbsp;//--&#62;Писатель. Пауза:&nbsp;&nbsp;1.&nbsp;&nbsp;T (млс.): 1977. Обработано записей: 10000. Время ЦП (млс.): 0.0686
&nbsp;&nbsp;//Читатель 3. Пауза:&nbsp;&nbsp;50.&nbsp;&nbsp;T (млс.): 2023. Обработано записей: 10000. Время ЦП (млс.): 1.3174
&nbsp;&nbsp;//Читатель 1. Пауза:&nbsp;&nbsp; 5.&nbsp;&nbsp;T (млс.): 2023. Обработано записей: 10000. Время ЦП (млс.): 1.1746
&nbsp;&nbsp;//Читатель 4. Пауза: 100.&nbsp;&nbsp;T (млс.): 2023. Обработано записей: 10000. Время ЦП (млс.): 1.2129
&nbsp;&nbsp;//Читатель 2. Пауза:&nbsp;&nbsp;10.&nbsp;&nbsp;T (млс.): 2023. Обработано записей: 10000. Время ЦП (млс.): 1.2194
&nbsp;&nbsp;// -------------------------------------------------------------------------------

//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Краткое ТЗ на разработку очередей 
// 1) Создаются несколько циклических информационных очередей с разными типами данных и общим объектом 
// управления ими.
// 2) В очереди пишет данные один поток, а читают эти очереди несколько потоков.
// 3) Дополнительно создается специальная служебная циклическая очередь: битовые шкалы непустых информационных 
// очередей, записываемых пишущим потоком, после записи в информационные очереди.
// 4) У читающих потоков должны быть свои указатели чтения циклических очередей и их локальные битовые шкалы 
// непустых очередей, хранящиеся в их объекте доступа к информационным очередям.
// 5) Читающие потоки циклически, выполнив свои коды переходят в состояние ожидания сигнала появления данных 
// в их очередях или истечения заданного интервала времени на их общем объекте ожидания.
// 6) При записи в любую очередь пишущий поток после записи в информационные очереди записывает битовые шкалы 
// непустых очередей в служебную очередь шкал непустых очередей и активирует все читающие потоки.
// 7) Каждый читающий поток, при активации, читает все появившиеся записи в служебной очереди шкал непустых 
// очередей и формирует свою локальную шкалу непустых очередей и далее, на ее основании, читает без 
// синхронизации, непустые очереди с признаком : состояние прочитанных очередей в виде размера непрочитанных 
// данных.
// 8) Реализовать эффективное управление памятью при передаче данных в этих очередях.

//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; C++20 (используется std::countr_zero) ---
#include &#60;shlobj.h&#62;
#include &#60;windows.h&#62;&#62;
#include &#60;iostream&#62;
#include &#60;vector&#62;
#include &#60;thread&#62;
#include &#60;chrono&#62;
#include &#60;mutex&#62;
&nbsp;&nbsp; //#include &#60;condition_variable&#62;
&nbsp;&nbsp; //#include &#60;atomic&#62;
&nbsp;&nbsp; //#include &#60;bitset&#62;
&nbsp;&nbsp; //#include &#60;intrin.h&#62;
#pragma comment(lib, "winmm.lib")&nbsp;&nbsp;&nbsp;&nbsp;// Установить точность таймера 1 мс

// Параметры тестирования очередей ---
const int N_MAX = 1000;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Количество циклов записи при тестировании
const INT64 Q_SIZE = 64;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Длина информационной очереди по умолчанию 
const INT64 Q_SIZE_SL = 1024; // Длина служебной очереди шкал непустых информационных очередей
const int Q_MAX = 128;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Максимальное количество очередей
const int Количество_очередей = 120; // Количество используемых очередей ( &#60;= Q_MAX)
const int LEN_STR_MAX = 4096; // Размер буфера потоков чтения (максимум size_max_str всех используемых очередей) 
const double FILLING_THRESHOLD = 0.4; // Порог заполненности очередей для выдачи предупреждения об уровне заполненности очередей

// Системное время (синхронизированное). Точность 0,1 мкс.---
static INT64 T_OS_high_mls()
{
&nbsp;&nbsp; FILETIME lpSystemTimeAsFileTime;
&nbsp;&nbsp; GetSystemTimePreciseAsFileTime(&#38;lpSystemTimeAsFileTime);&nbsp;&nbsp;// Системное время (синхронизированное)
&nbsp;&nbsp; INT64 tt = lpSystemTimeAsFileTime.dwHighDateTime;
&nbsp;&nbsp; tt = (tt &#60;&#60; 32) + lpSystemTimeAsFileTime.dwLowDateTime;
&nbsp;&nbsp; return tt;
}

// Строка-объект информационной очереди (переиспользуемая) --
struct q_str {
&nbsp;&nbsp; INT64 tail = 0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Указатель записи ---
&nbsp;&nbsp; INT64 size_max = 0;&nbsp;&nbsp;// Максимальный размер памяти строки (с учетом символа конца строки '&#92;0')
&nbsp;&nbsp; INT64 size = 0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Текущий размер памяти строки (с учетом символа конца строки '&#92;0')
&nbsp;&nbsp; int n_q = 0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Номер очереди&nbsp;&nbsp;
&nbsp;&nbsp; char* str = NULL;&nbsp;&nbsp;&nbsp;&nbsp;// Память строки (размер len_max) ----
&nbsp;&nbsp; int state = 0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Состояние (не используется, но на всякий случай): 1 - занята пишущим потоком; 2 - в очереди.
};

// Параметры очереди, задаваемые при ее инициализации --
struct q_parm {
&nbsp;&nbsp; std::string name_Queue = "";&nbsp;&nbsp;&nbsp;&nbsp; // имя очереди
&nbsp;&nbsp; INT64 q_size = 0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// длина очереди
&nbsp;&nbsp; INT64 size_max_str = 64;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // максимальная память строки в информационной очереди
};

// Шаблонная циклическая очередь (Lock-free)
// Используется для информационных очередей и служебной
template&#60;typename T&#62;
class InfoQueue {
public:
&nbsp;&nbsp; std::vector&#60;T&#62; buffer;
&nbsp;&nbsp; INT64 tail = 0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // пишет только один писатель
&nbsp;&nbsp; std::string name_Queue = "";&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// имя очереди (можно менять при начальной инициализации)
&nbsp;&nbsp; INT64 q_size = 64;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// размер очереди (можно менять при начальной инициализации)
&nbsp;&nbsp; INT64 size_max_str = 64;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// максимальная память строки в очереди (можно менять при начальной инициализации)
&nbsp;&nbsp; InfoQueue(INT64 s = Q_SIZE) : buffer(s), q_size(s) {}

&nbsp;&nbsp; // Изменение длины очереди ---
&nbsp;&nbsp; void q_size_set(int s) { 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;buffer.reserve(s); q_size = s;
&nbsp;&nbsp; }

&nbsp;&nbsp; // запись в очередь --
&nbsp;&nbsp; bool push(T val) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (tail &#60; q_size)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; buffer&#91;tail % q_size&#93; = val;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;++tail;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return true;
&nbsp;&nbsp; }

&nbsp;&nbsp; // Количества доступных элементов для конкретного читателя
&nbsp;&nbsp; // local_head - указатель чтения (читающего потока)
&nbsp;&nbsp; // Если отрицательное значение, то: количество пропущенных при чтении записей очереди --
&nbsp;&nbsp; INT64 get_pending_count(INT64 local_head) const {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;INT64 t = tail - local_head;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return (t &#60;= q_size ? t : q_size - t);
&nbsp;&nbsp; }
};

// Копировать строку-объект в буфер читающего потока для последующей обработки ---
void q_str_copy(q_str* s, q_str* s1, InfoQueue&#60;q_str*&#62;* q)
{
&nbsp;&nbsp; //s1-&#62;size_max = s-&#62;size_max;&nbsp;&nbsp; // ! Задана при создании буфере --
&nbsp;&nbsp; s1-&#62;size = s-&#62;size;
&nbsp;&nbsp; s1-&#62;n_q = s-&#62;n_q;
&nbsp;&nbsp; if (s1-&#62;str == NULL) throw std::runtime_error("*** Ошибка: нет памяти для копирования");
&nbsp;&nbsp; //&nbsp;&nbsp;s1-&#62;size - размер памяти строки (с учетом символа конца строки '&#92;0')
&nbsp;&nbsp; //&nbsp;&nbsp;Длина строки: (s1-&#62;size - 1)&nbsp;&nbsp;
&nbsp;&nbsp; memcpy(s1-&#62;str, s-&#62;str, s1-&#62;size);&nbsp;&nbsp;s1-&#62;tail = s-&#62;tail;&nbsp;&nbsp; // ! Обязательно в таком порядке --
&nbsp;&nbsp; s1-&#62;state = s-&#62;state;
&nbsp;&nbsp; // Дополнительный контроль переполнение очереди (после копирования в буфер потока чтения)
&nbsp;&nbsp; if ((q-&#62;tail - s1-&#62;tail) &#62; q-&#62;q_size) std::cout &#60;&#60; " *** Ошибка переполнение очереди (при копировании) " &#60;&#60; s1-&#62;n_q &#60;&#60; "&#92;n";
}

// Информационные очереди событий --
struct DataCluster {
&nbsp;&nbsp; //&nbsp;&nbsp;Деструктор (! выход из блока кода, в котором определен DataCluster 
&nbsp;&nbsp; // только при завершения потоков, использующих его) --
&nbsp;&nbsp; ~DataCluster() { 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (int i = 0; i &#60; Q_MAX; ++i) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; INT64 size = q&#91;i&#93;.buffer.size();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; INT64 N = (q&#91;i&#93;.tail &#62;= size) ? size : q&#91;i&#93;.tail;&nbsp;&nbsp;// Буфер может быть неполным ---
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int j = 0; j &#60; N; ++j) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;q_str* q_s = q&#91;i&#93;.buffer&#91;1&#93;; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;delete&#91;&#93;q_s-&#62;str;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;delete q_s;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp; }

&nbsp;&nbsp; int count_q = 64;&nbsp;&nbsp;// Количество используемых информационных очередей (&#60;= Q_MAX) --
&nbsp;&nbsp; // При количестве очередей &#62; 64 один бит шкалы непустых очередей соотносится 
&nbsp;&nbsp; // к нескольким ((count_q - 1) / 64 + 1) последовательным очередям, в которых, 
&nbsp;&nbsp; // возможно, есть записи.
&nbsp;&nbsp; UINT32 Queue_grouping = 1; 
&nbsp;&nbsp; InfoQueue&#60;q_str*&#62; q&#91;Q_MAX&#93;;

&nbsp;&nbsp; // Задать количество используемых очередей и их параметры
&nbsp;&nbsp; void set_parm_q(int n, q_parm* parm) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (n &#62; 0 &#38;&#38; n &#60;= Q_MAX)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; count_q = n; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw std::runtime_error(" Количество используемых очередей &#62; Q_MAX или &#60;= 0");&nbsp;&nbsp; /* #### ошибка*/
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Queue_grouping = (count_q - 1) / 64 + 1;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (parm != NULL)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i = 0; i &#60; count_q; ++i) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;q&#91;i&#93;.name_Queue = parm-&#62;name_Queue;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;q&#91;i&#93;.q_size = parm-&#62;q_size;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;q&#91;i&#93;.q_size_set(parm-&#62;q_size);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;q&#91;i&#93;.size_max_str = parm-&#62;size_max_str;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp; }

&nbsp;&nbsp; // Функция формирования маски группировки очередей (многозначное отображение очередей на шкалу непустых)
&nbsp;&nbsp; uint64_t q_mask(int q_i) { return (uint64_t)1 &#60;&#60; (q_i / Queue_grouping); }

&nbsp;&nbsp; //&nbsp;&nbsp;Запрос памяти потоком писателем под запись очереди --
&nbsp;&nbsp; // Запрос памяти системы и формирование строки-объекта -----
&nbsp;&nbsp; q_str* get_q_s(int q_i) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;INT64 size_max_str = q&#91;q_i&#93;.size_max_str;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char* str = new char&#91;size_max_str&#93;;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;str&#91;size_max_str - 1&#93; = '&#92;0';
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;q_str* q_s = new q_str;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;q_s-&#62;str = str;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;q_s-&#62;size_max = size_max_str;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;q_s-&#62;size = 0;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;q_s-&#62;n_q = q_i;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return q_s;
&nbsp;&nbsp; }

&nbsp;&nbsp; // Запрос строки-объекта потоком записи для формирования строки 
&nbsp;&nbsp; q_str* get_str(int q_i) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;q_str* q_s;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;INT64 tail = q&#91;q_i&#93;.tail;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;INT64 t = tail % q&#91;q_i&#93;.q_size;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (q&#91;q_i&#93;.buffer&#91;t&#93; != NULL) {&nbsp;&nbsp; //&nbsp;&nbsp;Получение памяти из очереди --
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; q_s = q&#91;q_i&#93;.buffer&#91;t&#93;; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; q_s-&#62;tail = tail;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else {&nbsp;&nbsp; // Запрос памяти у системы --
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; q_s = get_q_s(q_i);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; q_s-&#62;tail = tail;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return q_s;
&nbsp;&nbsp; }

&nbsp;&nbsp; //&nbsp;&nbsp;Инициализация используемых очередей строками-объектами --
&nbsp;&nbsp; // #### Вряд ли стоит использовать ---
&nbsp;&nbsp; void initialization_q_str() {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (count_q &#62; 0) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i = 0; i &#60; count_q; ++i) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int size = q&#91;i&#93;.q_size;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (int j = 0; j &#60; size; ++j) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (q&#91;i&#93;.buffer&#91;j&#93; == NULL)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;q&#91;i&#93;.buffer&#91;j&#93; = get_q_s(i);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp; }

&nbsp;&nbsp; // Начальная инициализация указателей чтения в потоке-читателе на основе указателей 
&nbsp;&nbsp; // записи в очереди (при начальной инициализации потока)
&nbsp;&nbsp; void init_access(INT64 *tail_v) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (count_q &#60;= 0)&nbsp;&nbsp;throw std::runtime_error("*** Ошибка: не задано количество используемых очередей");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (int i = 0; i &#60; count_q; ++i) tail_v&#91;i&#93; = q&#91;i&#93;.tail;
&nbsp;&nbsp; }

&nbsp;&nbsp; // запись в очередь --
&nbsp;&nbsp; bool push(int q_n, q_str* val) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (q_n &#62;= count_q) throw std::runtime_error("*** Ошибка: нет такой очереди");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (q&#91;q_n&#93;.tail &#60; q&#91;q_n&#93;.q_size)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; q&#91;q_n&#93;.buffer&#91;q&#91;q_n&#93;.tail % q&#91;q_n&#93;.q_size&#93; = val;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;++q&#91;q_n&#93;.tail;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return true;
&nbsp;&nbsp; }
};

struct ReaderAccess;&nbsp;&nbsp; // Используется в Manager
// Объект управления потоками чтения 
struct Manager {
&nbsp;&nbsp; INT64 signal_counter = 0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Счетчик сигналов записи
&nbsp;&nbsp; std::mutex mtx;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Для синхронизации&nbsp;&nbsp;при ожидании&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp; std::condition_variable cv;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Общий объект ожидания&nbsp;&nbsp;
&nbsp;&nbsp; InfoQueue&#60;uint64_t&#62; service_q{ Q_SIZE_SL }; // Служебная очередь битовых шкал

&nbsp;&nbsp; void wait(ReaderAccess* access, int tt);&nbsp;&nbsp;// !! Реализация после struct ReaderAccess (иначе недоступны поля)

&nbsp;&nbsp; void pulse(uint64_t mask) { service_q.push(mask); ++signal_counter;&nbsp;&nbsp;cv.notify_all(); }
&nbsp;&nbsp; ////&nbsp;&nbsp;------- win32 API --
&nbsp;&nbsp; //HANDLE cv = CreateEvent(NULL, FALSE, FALSE, NULL);&nbsp;&nbsp;// Общий объект ожидания
&nbsp;&nbsp; //void pulse() { ++signal_counter; PulseEvent(cv); }
private:
&nbsp;&nbsp; INT64 tt = T_OS_high_mls();&nbsp;&nbsp;// Для коррекции интервала ожидания --
};

// Объект доступа к очередям для читающих потоков --
struct ReaderAccess {
&nbsp;&nbsp; INT64 h&#91;Q_MAX&#93; = { 0 };&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Локальные указатели чтения
&nbsp;&nbsp; INT64 h_service = 0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Указатель чтения служебной очереди
&nbsp;&nbsp; uint64_t local_bitmask = 0;&nbsp;&nbsp;&nbsp;&nbsp;// Локальная шкала непустых очередей потока-читателя
&nbsp;&nbsp; INT64 signal_counter = 0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Локальный счетчик сигналов записи 
&nbsp;&nbsp; //&nbsp;&nbsp;-------- Статистика очередей ---------
&nbsp;&nbsp; INT64 NN&#91;Q_MAX&#93; = { 0 };&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Коичество обращений к непустым очередям
&nbsp;&nbsp; double statistics&#91;Q_MAX&#93; = { 0 }; // Сумма относительной заполненности очередей (среднее = statistics &#91;i&#93;/ (NN&#91;i&#93;))
&nbsp;&nbsp; double q_filling_threshold = 0.4; // Порог заполненности очередей для выдачи предупреждения --
&nbsp;&nbsp; int period_agr = 9;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Период скользящей 
&nbsp;&nbsp; //&nbsp;&nbsp;------- Данные снимка состояния очередей потока чтения ---
&nbsp;&nbsp; int q_len&#91;Q_MAX&#93; = { 0 };&nbsp;&nbsp; // Количество непустых непрочитанных записей в очередях
&nbsp;&nbsp; int q_n&#91;Q_MAX&#93; = { 0 };&nbsp;&nbsp;&nbsp;&nbsp; // Номера непустых очередей
&nbsp;&nbsp; int q_p = 0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;Длина векторов q_n и q_len

&nbsp;&nbsp; // Снимок состояния очередей&nbsp;&nbsp;потока-читателя ("мгновенный", чтобы меньше было повторных чтений очередей).
&nbsp;&nbsp; void snapshot_queue_state(DataCluster&#38; dc, Manager&#38; mgr) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;q_p = 0;&nbsp;&nbsp;// Сброс длины векторов q_n и q_len
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Формирование шкалы непустых очередей потока-читателя
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;uint64_t mask_val;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;INT64 count = mgr.service_q.get_pending_count(h_service);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (count &#62; 0) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Формируем общую локальную шкалу непустых очередей потока-читателя --
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i = 0; i &#60; count; ++i) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mask_val = mgr.service_q.buffer&#91;h_service % mgr.service_q.q_size&#93;;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;local_bitmask |= mask_val; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;++h_service;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Формирование снимка состояния очередей потока-читателя (на основе его шкалы) --
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int pos_old = 0;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int Queue_grouping = dc.Queue_grouping;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while (local_bitmask) {&nbsp;&nbsp; //&nbsp;&nbsp;Эффективная обработка шкалы непустых очередей --
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;////&nbsp;&nbsp;! Способ определения позиции младшего разряда для старых стандартов (MSVC)&nbsp;&nbsp;-----
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// unsigned long pos3 = 0;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// _BitScanForward64(&#38;pos3, local_bitmask);&nbsp;&nbsp;// позиция младшего разряда с 1
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;////&nbsp;&nbsp;-------------------------------------------------
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int Сдвиг = std::countr_zero(local_bitmask); // C++20 (позиция младшего разряда с 1)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int pos = Сдвиг + pos_old;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pos_old = pos + 1;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pos *= Queue_grouping;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (int i = 0; i &#60; Queue_grouping; ++i) {&nbsp;&nbsp;// обработка групп очередей разряда шкалы --
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; INT64 q_count = dc.q&#91;pos + i&#93;.get_pending_count(h&#91;pos + i&#93;);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (q_count &#62; 0) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int p = q_p;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;q_n&#91;p&#93; = pos + i;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;q_len&#91;p&#93; = q_count;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;Статистика заполненности очереди (относительная, приблизительно средне скользящая) 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (++NN&#91;pos + i&#93; &#60; period_agr) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; statistics&#91;pos + i&#93; += (double)q_count / (double)dc.q&#91;pos + i&#93;.q_size;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; statistics&#91;pos + i&#93; += (double)q_count / (double)dc.q&#91;pos + i&#93;.q_size - statistics&#91;pos + i&#93; / period_agr;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;++q_p;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (q_count &#60; 0)&nbsp;&nbsp;std::cout &#60;&#60; " *** Ошибка переполнение очереди (при чтении) q = " &#60;&#60; pos + i &#60;&#60; ": q_count = " &#60;&#60; q_count &#60;&#60; "&#92;n";&nbsp;&nbsp;// #### Ошибка переполнение очереди
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;local_bitmask &#62;&#62;= Сдвиг + 1;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else if (count &#60; 0)&nbsp;&nbsp;std::cout &#60;&#60; " *** -- Ошибка переполнение служебной очереди шкал --&#92;n";
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//std::cout &#60;&#60; ".&nbsp;&nbsp;Шкала : " &#60;&#60; std::bitset&#60;64&#62;(local_bitmask) &#60;&#60; "&#92;n";
&nbsp;&nbsp; }

&nbsp;&nbsp; // Чтение строки-объекта очереди --
&nbsp;&nbsp; // q_n - номер очереди
&nbsp;&nbsp; q_str* get_q_str(int q_n, DataCluster&#38; dc) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;q_str* val = dc.q&#91;q_n&#93;.buffer&#91;h&#91;q_n&#93; % dc.q&#91;q_n&#93;.q_size&#93;;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;++h&#91;q_n&#93;;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return val;
&nbsp;&nbsp; }
};

// ! Реализация члена Manager (расположена после struct ReaderAccess чтобы в методе были 
// доступны ее поля)
// Пауза (подстраиваемая с учетом длительности цикла обработки)
void Manager::wait(ReaderAccess* access, int dt) {
&nbsp;&nbsp; std::unique_lock&#60;std::mutex&#62; lock(mtx);
&nbsp;&nbsp; if (access-&#62;signal_counter == signal_counter) {// не было сигнала записи при отсутствии ожидания в потоке чтения
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Вычисление коррекции паузы с учетом времени обработки цикла --
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tt = (T_OS_high_mls() - tt) * 0.0001;&nbsp;&nbsp;// время обработки потока в цикле (млс.) --
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tt = (dt - tt);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tt = tt &#62; 0 ? tt : 1;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cv.wait_for(lock, std::chrono::milliseconds(tt)) == std::cv_status::timeout;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//WaitForSingleObject(cv, tt); // вместо предыдущего оператора с комментированием первого оператора метода
&nbsp;&nbsp; }
&nbsp;&nbsp; tt = T_OS_high_mls();
&nbsp;&nbsp; access-&#62;signal_counter = signal_counter;&nbsp;&nbsp; // Обрабатываемый сигнал записи в потоке чтения
}

//&nbsp;&nbsp;Тестирование очередей событий&nbsp;&nbsp;------------------------------------------------
//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Функция потока-писателя
// Формат записываемых строк общий для всех очередей: тексты таблиц Lua (сереализация).
// Читающий поток выполняет десереализацию полученных строк и запуск Lua-функций обработки событий
void producer(DataCluster&#38; dc, Manager&#38; mgr) {
&nbsp;&nbsp; std::cout &#60;&#60; "Писатель. Запись выполняется в 10 очередей. Циклов записи: " &#60;&#60; N_MAX &#60;&#60; std::endl;
&nbsp;&nbsp; std::cout &#60;&#60; "T - время выполнения потоков с учетом их пауз." &#60;&#60; std::endl;
&nbsp;&nbsp; // -------------------------------------
&nbsp;&nbsp; INT64 tt = T_OS_high_mls();&nbsp;&nbsp;// Для коррекции интервала ожидания --
&nbsp;&nbsp; INT64 TT = tt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Для замера времени выполнения&nbsp;&nbsp;---
&nbsp;&nbsp; INT64 TCP = 0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Для замера времени ЦП&nbsp;&nbsp;---
&nbsp;&nbsp; INT64 NRQ = 0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Количество обработанных записей
&nbsp;&nbsp; int dt = 1;
&nbsp;&nbsp; for (int i = 1; i &#60;= N_MAX; ++i) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tt = T_OS_high_mls() - tt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TCP += tt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;std::this_thread::sleep_for(std::chrono::milliseconds(dt));&nbsp;&nbsp; // Пауза потока записи
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//Sleep(dt);&nbsp;&nbsp; // И так можно 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;%%%%%% Подготовка данных для записи в очереди в виде C-строк

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp; Запись строк в очереди --
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;uint64_t mask = 0;&nbsp;&nbsp;// шкала непустых очередей
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//// Запись в разные очереди
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// !!&nbsp;&nbsp;rec-&#62;size - размер памяти строки (с учетом символа конца строки '&#92;0')
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;&nbsp;&nbsp; Длина строки: (rec-&#62;size - 1) 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const char* source = "{name = 'OnCleanUp',tbl = {&nbsp;&nbsp;....&#93; }"; int sz = strlen(source) + 1;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;q_str* rec = dc.get_str(0); memcpy(rec-&#62;str, source, sz); rec-&#62;size = sz;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (dc.push(0, rec))&nbsp;&nbsp;mask |= dc.q_mask(0);&nbsp;&nbsp;++NRQ;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rec = dc.get_str(1);&nbsp;&nbsp;memcpy(rec-&#62;str, source, sz); rec-&#62;size = sz;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (dc.push(1, rec))&nbsp;&nbsp;mask |= dc.q_mask(1);&nbsp;&nbsp;++NRQ;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rec = dc.get_str(2);&nbsp;&nbsp;memcpy(rec-&#62;str, source, sz); rec-&#62;size = sz;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (dc.push(2, rec))&nbsp;&nbsp;mask |= dc.q_mask(2);&nbsp;&nbsp;++NRQ;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rec = dc.get_str(3);&nbsp;&nbsp;memcpy(rec-&#62;str, source, sz); rec-&#62;size = sz;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (dc.push(3, rec))&nbsp;&nbsp;mask |= dc.q_mask(3);&nbsp;&nbsp;++NRQ;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rec = dc.get_str(40);&nbsp;&nbsp;memcpy(rec-&#62;str, source, sz); rec-&#62;size = sz;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (dc.push(40, rec))&nbsp;&nbsp;mask |= dc.q_mask(40);&nbsp;&nbsp;++NRQ;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rec = dc.get_str(41);&nbsp;&nbsp;memcpy(rec-&#62;str, source, sz); rec-&#62;size = sz;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (dc.push(41, rec))&nbsp;&nbsp;mask |= dc.q_mask(41);&nbsp;&nbsp;++NRQ;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rec = dc.get_str(40);&nbsp;&nbsp;memcpy(rec-&#62;str, source, sz); rec-&#62;size = sz;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (dc.push(40, rec))&nbsp;&nbsp;mask |= dc.q_mask(40);&nbsp;&nbsp;++NRQ;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rec = dc.get_str(41);&nbsp;&nbsp;memcpy(rec-&#62;str, source, sz); rec-&#62;size = sz;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (dc.push(41, rec))&nbsp;&nbsp;mask |= dc.q_mask(41);&nbsp;&nbsp;++NRQ;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rec = dc.get_str(42);&nbsp;&nbsp;memcpy(rec-&#62;str, source, sz); rec-&#62;size = sz;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (dc.push(42, rec))&nbsp;&nbsp;mask |= dc.q_mask(42);&nbsp;&nbsp;++NRQ;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rec = dc.get_str(100); memcpy(rec-&#62;str, "шшшшшш", 7); rec-&#62;size = 7;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (i &#62;= N_MAX) rec-&#62;state= -1;&nbsp;&nbsp; // Признак читателям завершить поток ---
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (dc.push(100, rec)) mask |= dc.q_mask(100);&nbsp;&nbsp;++NRQ;&nbsp;&nbsp; // "шшшшшш"
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// std::cout &#60;&#60; "&#91;P&#93; Данные записаны, маска: " &#60;&#60; std::bitset&#60;64&#62;(mask) &#60;&#60; std::endl;&nbsp;&nbsp;// ####
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Запись маски и активация потоков
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mgr.pulse(mask);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tt = T_OS_high_mls();
&nbsp;&nbsp; }
&nbsp;&nbsp; std::stringstream out;&nbsp;&nbsp; // можно использовать &#60;&#60;
&nbsp;&nbsp; out &#60;&#60; "---&#62;&nbsp;&nbsp; Писатель. Пауза:" &#60;&#60; dt
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#60;&#60; ". T (млс.):"
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#60;&#60; (int)((T_OS_high_mls() - TT) * 0.0001) &#60;&#60; ". Обработано записей:" &#60;&#60; NRQ
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#60;&#60; ". Время ЦП (млс.):" &#60;&#60; TCP * 0.0001 &#60;&#60; "&#92;n";
&nbsp;&nbsp; std::string result = out.str();&nbsp;&nbsp;&nbsp;&nbsp;// Получаем std::string
&nbsp;&nbsp; std::cout &#60;&#60; result;
}

// Заглушка функции обработки событий. 
//&nbsp;&nbsp; Выполняет десереализацию полученных строк и запуск Lua-функций обработки событий
// в зависимости от name (вида очереди)
//&nbsp;&nbsp;1) !! Память q_s возвращать не надо
//&nbsp;&nbsp;2) !! q_s-&#62;size - размер памяти строки (с учетом символа конца строки '&#92;0')
//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Длина строки: (q_s-&#62;size - 1) 
void event_handling(q_str* q_s, std::string name) {

}

//&nbsp;&nbsp;Функция потоков-читателей --
//&nbsp;&nbsp;filling_threshold - порог заполненности очередей (&#60;текущее количество записей очереди&#62; / &#60;размер очереди&#62;) 
//&nbsp;&nbsp;для выдачи предупреждений: "Превышен порог заполненности очередей" 
//&nbsp;&nbsp;dt - пауза (млс.)&nbsp;&nbsp; 
void consumer(int id, DataCluster&#38; dc, Manager&#38; mgr, double filling_threshold, int dt) {
&nbsp;&nbsp; ReaderAccess access;
&nbsp;&nbsp; access.q_filling_threshold = filling_threshold;
&nbsp;&nbsp; dc.init_access(access.h);&nbsp;&nbsp;// Инициализация access.h (указателей) потока чтения с учетом текущего состояния очередей --
&nbsp;&nbsp; // Буфер строки-объекта для дальнейшей обработки - q_str_c --
&nbsp;&nbsp; const char str_copy&#91;LEN_STR_MAX&#93; = {'&#92;0'};
&nbsp;&nbsp; q_str q_str_c;&nbsp;&nbsp;q_str_c.str = (char*)str_copy; q_str_c.size_max = LEN_STR_MAX;
&nbsp;&nbsp; // -------------------------------------
&nbsp;&nbsp; INT64 tt = T_OS_high_mls();&nbsp;&nbsp;// Для коррекции интервала ожидания --
&nbsp;&nbsp; INT64 TT = tt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Для замера времени выполнения&nbsp;&nbsp;---
&nbsp;&nbsp; INT64 TCP = 0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Для замера времени ЦП&nbsp;&nbsp;---
&nbsp;&nbsp; INT64 NRQ = 0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Количество обработанных записей
&nbsp;&nbsp; while (true) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tt = (T_OS_high_mls() - tt);&nbsp;&nbsp;// время обработки потока в цикле --
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TCP += tt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Пауза (подстраиваемая с учетом длительности цикла обработки) 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// с ожиданием сигнала записи в одну из информационных очередей --
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mgr.wait(&#38;access, dt);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tt = T_OS_high_mls(); 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;------------------------------------------------------------------
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp; Получение снимка состояния очередей потока ("мгновенный", 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;чтобы меньше было повторных чтений очередей).
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;access.snapshot_queue_state(dc, mgr);

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;Обработка снимка состояния очередей потока: 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;1) access.q_p - вектор номеров непустых очередей 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;2) access.q_len - вектор длин необработанных очередей
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;3) access.q_p - длина векторов 1), 2)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int q_p = access.q_p;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Просмотр и обработка вектора непустых очередей потока чтения
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (int i = 0; i &#60; q_p; ++i) {&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int q_n = access.q_n&#91;i&#93;;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int count = access.q_len&#91;i&#93;;&nbsp;&nbsp;// Количество записей в очереди --
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;&nbsp; ------- Сбор статистики ----
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; double statistics_agr = access.statistics&#91;q_n&#93; / (access.NN&#91;q_n&#93; &#60; access.period_agr ? access.NN&#91;q_n&#93; : access.period_agr);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (statistics_agr &#62; access.q_filling_threshold)&nbsp;&nbsp;//&nbsp;&nbsp;#### Предупреждающее сообщение --
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;std::cout &#60;&#60; "&#91;Поток: " &#60;&#60; id &#60;&#60; "&#93; Очередь "
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#60;&#60; q_n &#60;&#60; ".&nbsp;&nbsp;Заполненность (средняя): " &#60;&#60; statistics_agr
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#60;&#60; ".&nbsp;&nbsp;*** Превышен порог заполненности очередей: " &#60;&#60; access.q_filling_threshold &#60;&#60; "&#92;n";

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ////&nbsp;&nbsp; Отладочная печать 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //if (count &#62; 0 || count &#60; 0)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;&nbsp; std::cout &#60;&#60; "&#91;Поток: " &#60;&#60; id &#60;&#60; "&#93; Очередь "
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;&nbsp; &#60;&#60; q_n &#60;&#60; ".&nbsp;&nbsp;Заполненность (средняя): " &#60;&#60; statistics_agr
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;&nbsp; &#60;&#60; ".&nbsp;&nbsp;Количество: " &#60;&#60; count &#60;&#60; ". Ук чтения " &#60;&#60; access.h&#91;q_n&#93; &#60;&#60; "&#92;n";
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;&nbsp;--------------------
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; INT64 q_size = dc.q&#91;q_n&#93;.q_size;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Чтение и обработка очередей 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while (count--) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;q_str* val_q = access.get_q_str(q_n, dc);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp; Копировать запись очереди в буфер обработки q_str_c (типа q_str), 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// созданный в стеке --
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;q_str_copy(val_q, &#38;q_str_c, &#38;dc.q&#91;q_n&#93;);&nbsp;&nbsp;val_q = NULL;&nbsp;&nbsp; // ! далее использовать только q_str_c
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;event_handling(&#38;q_str_c, dc.q&#91;q_n&#93;.name_Queue);&nbsp;&nbsp; //&nbsp;&nbsp;Вызов функции обработки записи очереди
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;++NRQ;&nbsp;&nbsp; // Количество обработанных записей 

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Результат теста 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Завершение потока при тестировании --
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (q_str_c.state == -1) {&nbsp;&nbsp; // Признак читателям завершить поток ---
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; std::stringstream out;&nbsp;&nbsp;&nbsp;&nbsp;// можно использовать &#60;&#60;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out &#60;&#60; "Читатель " &#60;&#60; id &#60;&#60; ". Пауза:" &#60;&#60; dt
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#60;&#60; ". T (млс.):"
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#60;&#60; (int)((T_OS_high_mls() - TT) * 0.0001) &#60;&#60; ". Обработано записей:" &#60;&#60; NRQ
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#60;&#60; ". Время ЦП (млс.):" &#60;&#60; TCP * 0.0001 &#60;&#60; "&#92;n";
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; std::string result = out.str();&nbsp;&nbsp;&nbsp;&nbsp;// Получаем std::string
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; std::cout &#60;&#60; result;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;Отладочная печать 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//const char* val_str = q_str_c.str; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//if (access.h&#91;q_n&#93; == N_MAX) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp; std::cout &#60;&#60; " Поток " &#60;&#60; id &#60;&#60; ". Очередь " &#60;&#60; q_n &#60;&#60; " -&#62; Val(str): " &#60;&#60; val_str &#60;&#60; ". Указатель чтения "
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#60;&#60; access.h&#91;q_n&#93; &#60;&#60; ". Интервал (млс.) " &#60;&#60; (T_OS_high_mls() - TT) * 0.0001 &#60;&#60; "&#92;n";
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;----------------------------------------------------

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//// %%%%%%%%%%%% Обработка остальных данных в потоке чтения --&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp; }
}

//&nbsp;&nbsp; ---------------------&nbsp;&nbsp;Тест очередей&nbsp;&nbsp;-------------------------
int main() {
&nbsp;&nbsp; SetConsoleCP(1251);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Кодировка 1251 (! #include &#60;shlobj.h&#62;)
&nbsp;&nbsp; setlocale(LC_ALL, "");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // #### Русификация вывода ввода (работает) -----
&nbsp;&nbsp; timeBeginPeriod(1); // Установить точность 1 мс
&nbsp;&nbsp; // ==================================
&nbsp;&nbsp; DataCluster dc;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Очереди ---
&nbsp;&nbsp; //&nbsp;&nbsp;1. Для каждой очереди должен быть задан размер памяти для максимальной строки, передаваемой 
&nbsp;&nbsp; //&nbsp;&nbsp;&nbsp;&nbsp; в ней (с учетом признака конца строки '&#92;0'). 
&nbsp;&nbsp; //&nbsp;&nbsp;2. В вызове метода set_parm_q второй параметр: вектор типа q_parm * с параметрами очередей
&nbsp;&nbsp; //&nbsp;&nbsp;3. Порядок элементов в векторе определяет приоритет обработки очередей (0 -&#62; 1 ...)
&nbsp;&nbsp; q_parm* Параметры_очередей = NULL;&nbsp;&nbsp; // Параметры_очередей&#91;Количество_очередей&#93; 
&nbsp;&nbsp; dc.set_parm_q(Количество_очередей, Параметры_очередей);
&nbsp;&nbsp; //dc.initialization_q_str();&nbsp;&nbsp;&nbsp;&nbsp;// #### Вряд ли стоит использовать ---
&nbsp;&nbsp; Manager mgr;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Объект управления потоками чтения 

&nbsp;&nbsp; //&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Читающие потоки --
&nbsp;&nbsp; // Последний параметр интервал ожидания (млс.)
&nbsp;&nbsp; std::thread c1(consumer, 1, std::ref(dc), std::ref(mgr), FILLING_THRESHOLD, 5);
&nbsp;&nbsp; std::thread c2(consumer, 2, std::ref(dc), std::ref(mgr), FILLING_THRESHOLD, 10);
&nbsp;&nbsp; std::thread c3(consumer, 3, std::ref(dc), std::ref(mgr), FILLING_THRESHOLD, 50);
&nbsp;&nbsp; std::thread c4(consumer, 4, std::ref(dc), std::ref(mgr), FILLING_THRESHOLD, 100);
&nbsp;&nbsp; //&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Пишущий поток --
&nbsp;&nbsp; std::thread p(producer, std::ref(dc), std::ref(mgr));

&nbsp;&nbsp; p.join(); c1.join(); c2.join(); c3.join(); c4.join();
&nbsp;&nbsp; timeEndPeriod(1); // Освободить точность
&nbsp;&nbsp; char nazv_sh&#91;100&#93;;
&nbsp;&nbsp; std::cout &#60;&#60; "Введите что-нибудь:" &#60;&#60; std::endl;
&nbsp;&nbsp; std::cin &#62;&#62; nazv_sh;
&nbsp;&nbsp; return 0;
}
</pre>
============= <br />
			<i>09.03.2026 21:59:17, TGB.</i>]]></description>
			<link>http://forum.quik.ru/messages/forum10/message81923/topic8563/</link>
			<guid>http://forum.quik.ru/messages/forum10/message81923/topic8563/</guid>
			<pubDate>Mon, 09 Mar 2026 21:59:17 +0300</pubDate>
			<category>Программирование на языке Lua</category>
		</item>
		<item>
			<title>Если бы я был архитектором QUIK</title>
			<description><![CDATA[<b><a href="http://forum.quik.ru/messages/forum10/message81780/topic8563/">Если бы я был архитектором QUIK</a></b> <i> Что стоило бы изменить в QUIK по-крупному</i> в форуме <a href="http://forum.quik.ru/forum10/">Программирование на языке Lua</a>. <br />
			Вариант ТЗ на модификацию интерфейса QLua c QUIK<br />1. Вместо существующей схемы запуска коллбеков создаются циклические очереди (соответствующие коллбекам). <br />2. В очереди пишет данные событий служебный поток QUIK, а читают эти очереди и выполняют функции их обработки потоки main скриптов пользователя в теле &nbsp;модифицированной функции sleep. <br />3. В меню, добавленное в окно запуска пользовательских скриптов, обеспечить возможность изменения пользователем умолчания параметров очередей, а также функций фильтрации данных, записываемых в очереди .<br />4. У читающих потоков должны быть свои указатели чтения циклических очередей и битовые шкалы непустых очередей, хранящиеся в объекте доступа к их очередям. <br />5. Читающие потоки циклически, выполнив свои коды переходят в состояние ожидания сигнала появления данных в их очередях или истечения заданного интервала времени. <br />6. При записи в любую очередь пишущий поток меняет состояние битовых шкал непустых очередей и активирует все читающие потоки. <br />7. Каждый читающий поток при чтении очереди, выбираемой на основании своей битовой шкалы непустых очередей, получает признак: состояние прочитанных очередей в виде размера непрочитанных данных. <br />8. Добавить в код динамическое управление количеством очередей и реализовать полностью Lock-Free механизм уведомления без использования std::mutex. <br />9. Добавить логику удаления очередей «на лету» (dynamic removal) с использованием механизма безопасного освобождения памяти, такого как Hazard Pointers. <br />10. Реализовать идиому Hazard Pointers с очередью на удаление (retire list).<br />11. Реализовать пользовательскую функцию запроса свойств очередей.<br />12. Подключать в QUIKе коды Lua без внесения изменений в его исходники.<br />13. Перенести все необходимые функции (с внесением изменений, учитывающих п.1 - 12), реализованные в текущей версии QUIK в исходниках Lua, в пакет dll.<br />-------<br /> &nbsp; Что в написанном выше не понятно? В моих сообщениях 91 и 92 приведены "сырые", но работающие прототипы, частично реализующие это ТЗ. <br /> &nbsp;На возражения, замечания и предложения по улучшению ТЗ, постараюсь ответить. <br />
			<i>01.03.2026 11:28:58, TGB.</i>]]></description>
			<link>http://forum.quik.ru/messages/forum10/message81780/topic8563/</link>
			<guid>http://forum.quik.ru/messages/forum10/message81780/topic8563/</guid>
			<pubDate>Sun, 01 Mar 2026 11:28:58 +0300</pubDate>
			<category>Программирование на языке Lua</category>
		</item>
		<item>
			<title>Если бы я был архитектором QUIK</title>
			<description><![CDATA[<b><a href="http://forum.quik.ru/messages/forum10/message81776/topic8563/">Если бы я был архитектором QUIK</a></b> <i> Что стоило бы изменить в QUIK по-крупному</i> в форуме <a href="http://forum.quik.ru/forum10/">Программирование на языке Lua</a>. <br />
			Мне стало интересно, а более продвинутый вариант циклических очередей ИИ сможет реализовать?<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Промт для ИИ<br /> &nbsp;Написать программу на C++, в которой:<br /> &nbsp; &nbsp;1) Создаются несколько циклических очередей с разными типами данных.<br /> &nbsp; &nbsp;2) В очереди пишет данные один поток, а читают эти очереди несколько потоков.<br /> &nbsp; &nbsp;3) У читающих потов должны быть свои указатели чтения циклических очередей и битовые шкалы непустых очередей, хранящиеся в объекте доступа к их очередям.<br /> &nbsp; &nbsp;4) При записи в любую очередь пишущий поток меняет состояние битовых шкал непустых очередей и активирует все читающие потоки.<br /> &nbsp; &nbsp;5) Каждый читающий поток при чтении очереди, выбираемой на основании своей битовой шкалы непустых очередей, получает признак: состояние прочитанных очередей в виде размера непрочитанных данных.<br /> &nbsp; &nbsp;6) Добавить в код динамическое управление количеством очередей и реализовать полностью Lock-Free механизм уведомления без использования std::mutex.<br /><br />Смог &nbsp;(все комментарии ИИ):<br />
====code====
<pre>
// Это сложная задача, требующая использования атомарных операций (std::atomic) и барьеров памяти для реализации Lock-Free взаимодействия.
// Для выполнения условия динамического управления очередями и уведомления без мьютексов, мы будем использовать std::atomic_flag или
// std::atomic&#60;bool&#62; для сигнализации (busy-wait или wait/notify в C++20) и атомарные указатели.
// Ниже представлен концептуальный код, реализующий эту логику:
//&nbsp;&nbsp;&nbsp;&nbsp;C++20&nbsp;&nbsp; ----
#include &#60;iostream&#62;
#include &#60;vector&#62;
#include &#60;atomic&#62;
#include &#60;thread&#62;
#include &#60;memory&#62;
#include &#60;optional&#62;

// Шаблонная циклическая очередь (Lock-Free для одного писателя и нескольких читателей со своими индексами)
template&#60;typename T, size_t Capacity&#62;
class CircularQueue {
&nbsp;&nbsp;&nbsp;&nbsp;T buffer&#91;Capacity&#93;;
&nbsp;&nbsp;&nbsp;&nbsp;std::atomic&#60;size_t&#62; head{0}; // Указатель записи

public:
&nbsp;&nbsp;&nbsp;&nbsp;void push(T item) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;size_t h = head.load(std::memory_order_relaxed);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;buffer&#91;h % Capacity&#93; = item;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;head.store(h + 1, std::memory_order_release);
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;bool try_pop(size_t&#38; reader_idx, T&#38; out_item, size_t&#38; pending_count) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;size_t current_head = head.load(std::memory_order_acquire);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (reader_idx &#60; current_head) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;out_item = buffer&#91;reader_idx % Capacity&#93;;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;reader_idx++;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pending_count = current_head - reader_idx;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return true;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pending_count = 0;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return false;
&nbsp;&nbsp;&nbsp;&nbsp;}
};

// Объект доступа для каждого читающего потока
struct ReaderContext {
&nbsp;&nbsp;&nbsp;&nbsp;std::atomic&#60;uint64_t&#62; bitmask{0}; // Битовая шкала непустых очередей
&nbsp;&nbsp;&nbsp;&nbsp;std::vector&#60;size_t&#62; read_indices;&nbsp;&nbsp;// Свои указатели чтения
&nbsp;&nbsp;&nbsp;&nbsp;std::atomic&#60;bool&#62; data_ready{false};

&nbsp;&nbsp;&nbsp;&nbsp;void update_mask(size_t queue_idx) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bitmask.fetch_or(1ULL &#60;&#60; queue_idx, std::memory_order_release);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;data_ready.store(true, std::memory_order_release);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;data_ready.notify_all(); // C++20 Lock-free уведомление
&nbsp;&nbsp;&nbsp;&nbsp;}
};

class QueueManager {
&nbsp;&nbsp;&nbsp;&nbsp;CircularQueue&#60;int, 1024&#62; int_queue;
&nbsp;&nbsp;&nbsp;&nbsp;CircularQueue&#60;float, 1024&#62; float_queue;
&nbsp;&nbsp;&nbsp;&nbsp;std::vector&#60;std::shared_ptr&#60;ReaderContext&#62;&#62; readers;

public:
&nbsp;&nbsp;&nbsp;&nbsp;void add_reader(std::shared_ptr&#60;ReaderContext&#62; ctx) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ctx-&#62;read_indices.resize(2, 0); // 0 для int, 1 для float
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;readers.push_back(ctx);
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;void write_int(int val) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int_queue.push(val);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (auto&#38; r : readers) r-&#62;update_mask(0);
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;void write_float(float val) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;float_queue.push(val);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (auto&#38; r : readers) r-&#62;update_mask(1);
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;void reader_thread_func(std::shared_ptr&#60;ReaderContext&#62; ctx, int id) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;while (true) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Ожидание данных (Lock-free wait)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ctx-&#62;data_ready.wait(false, std::memory_order_acquire);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;uint64_t mask = ctx-&#62;bitmask.load(std::memory_order_acquire);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (size_t i = 0; i &#60; 2; ++i) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (mask &#38; (1ULL &#60;&#60; i)) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;size_t pending = 0;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (i == 0) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int val;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;while (int_queue.try_pop(ctx-&#62;read_indices&#91;0&#93;, val, pending)) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("&#91;Reader %d&#93; Int: %d (Pending: %zu)&#92;n", id, val, pending);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} else {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;float val;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;while (float_queue.try_pop(ctx-&#62;read_indices&#91;1&#93;, val, pending)) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("&#91;Reader %d&#93; Float: %.2f (Pending: %zu)&#92;n", id, val, pending);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Сбрасываем бит, если данных больше нет
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (pending == 0) ctx-&#62;bitmask.fetch_and(~(1ULL &#60;&#60; i));
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ctx-&#62;data_ready.store(false, std::memory_order_relaxed);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;}
};

int main() {
&nbsp;&nbsp;&nbsp;&nbsp;QueueManager manager;
&nbsp;&nbsp;&nbsp;&nbsp;auto r1 = std::make_shared&#60;ReaderContext&#62;();
&nbsp;&nbsp;&nbsp;&nbsp;auto r2 = std::make_shared&#60;ReaderContext&#62;();

&nbsp;&nbsp;&nbsp;&nbsp;manager.add_reader(r1);
&nbsp;&nbsp;&nbsp;&nbsp;manager.add_reader(r2);

&nbsp;&nbsp;&nbsp;&nbsp;std::thread t1(&#38;QueueManager::reader_thread_func, &#38;manager, r1, 1);
&nbsp;&nbsp;&nbsp;&nbsp;std::thread t2(&#38;QueueManager::reader_thread_func, &#38;manager, r2, 2);

&nbsp;&nbsp;&nbsp;&nbsp;// Пишущий поток
&nbsp;&nbsp;&nbsp;&nbsp;std::thread writer(&#91;&#38;&#93;() {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (int i = 0; i &#60; 5; ++i) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;std::this_thread::sleep_for(std::chrono::milliseconds(100));
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;manager.write_int(100 + i);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;manager.write_float(1.5f * i);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;});

&nbsp;&nbsp;&nbsp;&nbsp;writer.join();
&nbsp;&nbsp;&nbsp;&nbsp;t1.detach(); 
&nbsp;&nbsp;&nbsp;&nbsp;t2.detach();

&nbsp;&nbsp;&nbsp;&nbsp;return 0;
}
// Ключевые моменты реализации :
// Индивидуальные индексы : Каждый читатель хранит свои read_indices, что позволяет им двигаться по одной и 
// той же очереди независимо.
// Битовая шкала : Используется std::atomic&#60;uint64_t&#62;, где каждый бит соответствует ID очереди.Это позволяет 
// мгновенно проверить, где появились данные.
// Lock - Free Notify : Использован метод atomic::wait / notify_all(доступен с C++20).Он работает эффективнее 
// обычного yield, так как позволяет потоку "заснуть" без использования мьютекса в пользовательском 
// коде(на уровне ОС это может использовать фьютексы).
// Pending Count : Метод try_pop возвращает разницу между указателем записи и текущим индексом чтения, 
// выполняя условие получения "размера непрочитанных данных".
</pre>
============= <br />
			<i>28.02.2026 15:33:43, TGB.</i>]]></description>
			<link>http://forum.quik.ru/messages/forum10/message81776/topic8563/</link>
			<guid>http://forum.quik.ru/messages/forum10/message81776/topic8563/</guid>
			<pubDate>Sat, 28 Feb 2026 15:33:43 +0300</pubDate>
			<category>Программирование на языке Lua</category>
		</item>
		<item>
			<title>Если бы я был архитектором QUIK</title>
			<description><![CDATA[<b><a href="http://forum.quik.ru/messages/forum10/message81775/topic8563/">Если бы я был архитектором QUIK</a></b> <i> Что стоило бы изменить в QUIK по-крупному</i> в форуме <a href="http://forum.quik.ru/forum10/">Программирование на языке Lua</a>. <br />
			<br />====quote====<br /><a class="blog-p-user-name" id="bp_SzVKBkwO" href="/user/16979/" bx-tooltip-user-id="16979">Oleg Kuzembaev</a> написал:<br />необходимо достичь понимания, какая именно доработка требуется.Если у вас уже есть такая наготове, то просьба предметно описать ее: что именно стоит добавить? Как это будет выглядеть в вашем представлении?<br />=============<br />Промт для ИИ<br /> &nbsp;Написать программу на C++, в которой:<br /> &nbsp; &nbsp;1) Создаются несколько циклических очередей с разными типами данных.<br /> &nbsp; &nbsp;2) В очереди пишет данные один поток, а читают эти очереди несколько потоков.<br /> &nbsp; &nbsp;3) У читающих потов должны быть свои указатели чтения циклических очередей.<br /> &nbsp; &nbsp;4) При записи в любую очередь пишущий поток активирует все читающие потоки.<br /> &nbsp; &nbsp;5) Каждый читающий поток при чтении получает признак: состояние прочитанных очередей в виде размера <br /> &nbsp; &nbsp; &nbsp; непрочитанных данных.<br />--------<br />Результат от ИИ, полученный в течении 5 сек.(компилируемый, код-основа для разработки очередей событий):<br />
====code====
<pre>
#include &#60;iostream&#62;
#include &#60;vector&#62;
#include &#60;thread&#62;
#include &#60;mutex&#62;
#include &#60;condition_variable&#62;
#include &#60;atomic&#62;

template &#60;typename T, size_t Size&#62;
class CircularQueue {
public:
&nbsp;&nbsp;&nbsp;&nbsp;void push(T item) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;buffer&#91;head % Size&#93; = item;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;head.fetch_add(1, std::memory_order_release);
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;// Каждый читатель передает свой локальный индекс
&nbsp;&nbsp;&nbsp;&nbsp;bool try_read(size_t&#38; reader_idx, T&#38; out_item, size_t&#38; pending_count) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;size_t current_head = head.load(std::memory_order_acquire);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (reader_idx &#60; current_head) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;out_item = buffer&#91;reader_idx % Size&#93;;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;reader_idx++;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pending_count = current_head - reader_idx;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return true;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pending_count = 0;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return false;
&nbsp;&nbsp;&nbsp;&nbsp;}

private:
&nbsp;&nbsp;&nbsp;&nbsp;T buffer&#91;Size&#93;;
&nbsp;&nbsp;&nbsp;&nbsp;std::atomic&#60;size_t&#62; head{0};
};

// Контейнер для очередей разных типов
struct SharedData {
&nbsp;&nbsp;&nbsp;&nbsp;CircularQueue&#60;int, 100&#62; int_queue;
&nbsp;&nbsp;&nbsp;&nbsp;CircularQueue&#60;double, 100&#62; double_queue;
&nbsp;&nbsp;&nbsp;&nbsp;std::mutex mtx;
&nbsp;&nbsp;&nbsp;&nbsp;std::condition_variable cv;
&nbsp;&nbsp;&nbsp;&nbsp;bool stop = false;
};

void writer(SharedData&#38; data) {
&nbsp;&nbsp;&nbsp;&nbsp;for (int i = 1; i &#60;= 5; ++i) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;std::lock_guard&#60;std::mutex&#62; lock(data.mtx);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;data.int_queue.push(i * 10);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;data.double_queue.push(i * 1.1);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;std::cout &#60;&#60; "&#91;Writer&#93; Pushed data set " &#60;&#60; i &#60;&#60; std::endl;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;data.cv.notify_all(); // Активируем всех читателей
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;std::this_thread::sleep_for(std::chrono::milliseconds(100));
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;data.stop = true;
&nbsp;&nbsp;&nbsp;&nbsp;data.cv.notify_all();
}

void reader(int id, SharedData&#38; data) {
&nbsp;&nbsp;&nbsp;&nbsp;size_t my_int_ptr = 0;
&nbsp;&nbsp;&nbsp;&nbsp;size_t my_dbl_ptr = 0;
&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;while (true) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;std::unique_lock&#60;std::mutex&#62; lock(data.mtx);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;data.cv.wait(lock, &#91;&#38;&#93; { return data.stop || true; }); // Упрощенно: просыпаемся по сигналу

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int val;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;double dval;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;size_t pending;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Читаем из инт-очереди
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;while (data.int_queue.try_read(my_int_ptr, val, pending)) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;std::cout &#60;&#60; "Reader " &#60;&#60; id &#60;&#60; " got INT: " &#60;&#60; val &#60;&#60; " (Left: " &#60;&#60; pending &#60;&#60; ")&#92;n";
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Читаем из дабл-очереди
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;while (data.double_queue.try_read(my_dbl_ptr, dval, pending)) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;std::cout &#60;&#60; "Reader " &#60;&#60; id &#60;&#60; " got DBL: " &#60;&#60; dval &#60;&#60; " (Left: " &#60;&#60; pending &#60;&#60; ")&#92;n";
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (data.stop) break;
&nbsp;&nbsp;&nbsp;&nbsp;}
}

int main() {
&nbsp;&nbsp;&nbsp;&nbsp;SharedData data;
&nbsp;&nbsp;&nbsp;&nbsp;std::thread w(writer, std::ref(data));
&nbsp;&nbsp;&nbsp;&nbsp;std::thread r1(reader, 1, std::ref(data));
&nbsp;&nbsp;&nbsp;&nbsp;std::thread r2(reader, 2, std::ref(data));

&nbsp;&nbsp;&nbsp;&nbsp;w.join();
&nbsp;&nbsp;&nbsp;&nbsp;r1.join();
&nbsp;&nbsp;&nbsp;&nbsp;r2.join();
&nbsp;&nbsp;&nbsp;&nbsp;return 0;
}

// Основные моменты реализации (от ИИ):
// Индивидуальные указатели: Переменные my_int_ptr и my_dbl_ptr находятся в стеке каждого читающего потока. Они отслеживают прогресс конкретного 
// читателя относительно общей «головы» (head) очереди.
// Шаблон очереди: Позволяет создавать экземпляры для int, double или любых других типов в одном контейнере SharedData.
// Синхронизация: std::condition_variable используется для мгновенной активации читателей пишущим потоком.
// Статус очереди: Метод try_read возвращает pending_count, что позволяет читателю знать, сколько данных еще осталось в буфере именно для него.

</pre>
============= <br />
			<i>28.02.2026 10:42:54, TGB.</i>]]></description>
			<link>http://forum.quik.ru/messages/forum10/message81775/topic8563/</link>
			<guid>http://forum.quik.ru/messages/forum10/message81775/topic8563/</guid>
			<pubDate>Sat, 28 Feb 2026 10:42:54 +0300</pubDate>
			<category>Программирование на языке Lua</category>
		</item>
		<item>
			<title>Если бы я был архитектором QUIK</title>
			<description><![CDATA[<b><a href="http://forum.quik.ru/messages/forum10/message81615/topic8563/">Если бы я был архитектором QUIK</a></b> <i> Что стоило бы изменить в QUIK по-крупному</i> в форуме <a href="http://forum.quik.ru/forum10/">Программирование на языке Lua</a>. <br />
			<br />====quote====<br /><a class="blog-p-user-name" id="bp_FmEkJ12J" href="/user/13952/" bx-tooltip-user-id="13952">TGB</a> написал:<br />предложение можно сформулировать следующим образом: &nbsp; &nbsp; функциональность обработки событий QUIK расширить, добавив в API пользователя функцию задания свойств служебных очередей событий<br />=============<br /> &nbsp; Оформить это, наверное, лучше в виде меню задания свойств служебных очередей событий QLua, вызываемое в окне запуска скриптов пользователя, при отсутствии запущенных скриптов.<br /> &nbsp; Промежуточный итог обсуждения интерфейса QUIK со скриптами QLua, при реализации предложений этой ветки следующий:<br /> пользователям, при этом, вносить изменения в их существующие скрипты не требуется. <br />
			<i>24.02.2026 15:45:40, TGB.</i>]]></description>
			<link>http://forum.quik.ru/messages/forum10/message81615/topic8563/</link>
			<guid>http://forum.quik.ru/messages/forum10/message81615/topic8563/</guid>
			<pubDate>Tue, 24 Feb 2026 15:45:40 +0300</pubDate>
			<category>Программирование на языке Lua</category>
		</item>
		<item>
			<title>Если бы я был архитектором QUIK</title>
			<description><![CDATA[<b><a href="http://forum.quik.ru/messages/forum10/message81592/topic8563/">Если бы я был архитектором QUIK</a></b> <i> Что стоило бы изменить в QUIK по-крупному</i> в форуме <a href="http://forum.quik.ru/forum10/">Программирование на языке Lua</a>. <br />
			<table class="forum-spoiler"><thead onclick="if (this.nextSibling.style.display=='none') { this.nextSibling.style.display=''; BX.addClass(this, 'forum-spoiler-head-open'); } else { this.nextSibling.style.display='none'; BX.removeClass(this, 'forum-spoiler-head-open'); } BX.onCustomEvent('BX.Forum.Spoiler:toggle', [{node: this}]); event.stopPropagation();"><tr><th><div>Скрытый текст</div></th></tr></thead><tbody class="forum-spoiler" style="display:none;"><tr><td><br />====quote====<br /><a class="blog-p-user-name" id="bp_K1iKhlKb" href="/user/13952/" bx-tooltip-user-id="13952">TGB</a> написал:<br />похоже, что вы полуинтеллектуальный робот-спамер устаревшей версии, без способности учета контекста обсуждаемой темы при генерации спама<br />=============<br /><br />====quote====<br />Чат-боты глупеют в длинных диалогах<br /><br />Эксперты Microsoft Research и Salesforce изучили более 200 000 диалогов пользователей в популярных чат-ботах (GPT-4.1, Gemini 2.5 Pro, Claude 3.7 Sonnet, o3, DeepSeek R1 и Llama 4), и как оказалось – умные помощники дают 90% верных ответов на отдельные запросы.<br /><br /><I><B>Однако если общение превращается в полноценный диалог, то точность ИИ падает уже до 65%. В многоходовых обсуждениях нейросети начинают давать ответы, <U>даже не прочитав полностью вопрос пользователя</U>. К тому же они часто опираются на свой первый ответ, даже если пользователь сказал что он неверен. Кроме того, новые ответы в диалогах с каждым разом становятся все длиннее – ИИ начинают больше предполагать и галлюцинировать.</B></I> <br /><br />В итоге исследователи рекомендуют придерживаться старой истины: краткость – сестра таланта. Чем короче беседа с ИИ, тем лучше будет качество ответов.<br />=============</td></tr></tbody></table> <br />
			<i>21.02.2026 13:11:39, Йцукен.</i>]]></description>
			<link>http://forum.quik.ru/messages/forum10/message81592/topic8563/</link>
			<guid>http://forum.quik.ru/messages/forum10/message81592/topic8563/</guid>
			<pubDate>Sat, 21 Feb 2026 13:11:39 +0300</pubDate>
			<category>Программирование на языке Lua</category>
		</item>
		<item>
			<title>Если бы я был архитектором QUIK</title>
			<description><![CDATA[<b><a href="http://forum.quik.ru/messages/forum10/message81591/topic8563/">Если бы я был архитектором QUIK</a></b> <i> Что стоило бы изменить в QUIK по-крупному</i> в форуме <a href="http://forum.quik.ru/forum10/">Программирование на языке Lua</a>. <br />
			У меня было предложение, которое стоит повторить:<br /><br />====quote====<br /><a class="blog-p-user-name" id="bp_g4DcM3Ls" href="/user/13952/" bx-tooltip-user-id="13952">TGB</a> написал:<br /> &nbsp;На форумах ARQA для комментирующих пользователей ввести месячный лимит трафика, после которого не будет возможность вводить комментарии. &nbsp; Значением этого лимита могло быть: Годовой трафик nikolz / 12 / 10. Но, конечно, насчет значения лимита, решать ARQA. <br /> &nbsp;Наличие такого лимита, обеспечило бы: &nbsp; &nbsp;<br /> &nbsp; - экономию дискового пространства баз форумов; &nbsp; &nbsp; <br /> &nbsp;- автоматическое модерирование форумов за счет принуждения думать о краткости и четкости текстов, пишущих комментаторами; &nbsp; &nbsp; &nbsp; - &nbsp; &nbsp; - удобство для читающих комментарии, в которых будет меньше флуда.<br />=============<br /> &nbsp; Это может показаться неактуальным из-за снижения активности пишущих, но могло бы улучшить качество форумов в перспективе. <br />
			<i>21.02.2026 12:49:30, TGB.</i>]]></description>
			<link>http://forum.quik.ru/messages/forum10/message81591/topic8563/</link>
			<guid>http://forum.quik.ru/messages/forum10/message81591/topic8563/</guid>
			<pubDate>Sat, 21 Feb 2026 12:49:30 +0300</pubDate>
			<category>Программирование на языке Lua</category>
		</item>
		<item>
			<title>Если бы я был архитектором QUIK</title>
			<description><![CDATA[<b><a href="http://forum.quik.ru/messages/forum10/message81584/topic8563/">Если бы я был архитектором QUIK</a></b> <i> Что стоило бы изменить в QUIK по-крупному</i> в форуме <a href="http://forum.quik.ru/forum10/">Программирование на языке Lua</a>. <br />
			<br />====quote====<br /><a class="blog-p-user-name" id="bp_iv9vJ0BP" href="/user/17142/" bx-tooltip-user-id="17142">Йцукен</a> написал:<br />Логичней было бы, чтобы терминал не дублировал пропущенные колбэки, по которым скрипт не получит новых данных.<br />=============<br /> &nbsp; Это хорошее ваше предложение можно сформулировать следующим образом:<br /> &nbsp; &nbsp; функциональность обработки событий QUIK расширить, добавив в API пользователя функцию задания свойств служебных очередей событий:<br /> &nbsp;1) длин очередей;<br /> &nbsp;2) функций фильтрации событий перед их записью в соответствующие очереди;<br /> &nbsp;3) приоритетов обработки очередей;<br /> &nbsp;4) может быть что то еще..<br /> &nbsp;Все свойства имеют значение по умолчанию. <br />
			<i>20.02.2026 17:57:57, TGB.</i>]]></description>
			<link>http://forum.quik.ru/messages/forum10/message81584/topic8563/</link>
			<guid>http://forum.quik.ru/messages/forum10/message81584/topic8563/</guid>
			<pubDate>Fri, 20 Feb 2026 17:57:57 +0300</pubDate>
			<category>Программирование на языке Lua</category>
		</item>
		<item>
			<title>Если бы я был архитектором QUIK</title>
			<description><![CDATA[<b><a href="http://forum.quik.ru/messages/forum10/message81583/topic8563/">Если бы я был архитектором QUIK</a></b> <i> Что стоило бы изменить в QUIK по-крупному</i> в форуме <a href="http://forum.quik.ru/forum10/">Программирование на языке Lua</a>. <br />
			<br />====quote====<br /><a class="blog-p-user-name" id="bp_DlodSvtm" href="/user/13952/" bx-tooltip-user-id="13952">TGB</a> написал:<br />Вы сначала создаете таблицу фильтрации со значениями полей фильтрации заведомо не совпадающими с ожидаемыми данными.<br />=============<br />Ну т.е., фактически запросить данные, обработать их (как минимум сравнить с предыдущими).<br />Логичней было бы, чтобы терминал не дублировал пропущенные колбэки, по которым скрипт не получит новых данных.<br />Хотя, кому-то наоборот нужна вся история изменений (для индикатора какого-нибудь). Не зря же есть CreateDataSource для параметров инструментов. Но тогда QUIK должен хранить всю историю изменений.<br />В общем, этот момент вам надо продумать. <br />
			<i>20.02.2026 15:16:51, Йцукен.</i>]]></description>
			<link>http://forum.quik.ru/messages/forum10/message81583/topic8563/</link>
			<guid>http://forum.quik.ru/messages/forum10/message81583/topic8563/</guid>
			<pubDate>Fri, 20 Feb 2026 15:16:51 +0300</pubDate>
			<category>Программирование на языке Lua</category>
		</item>
		<item>
			<title>Если бы я был архитектором QUIK</title>
			<description><![CDATA[<b><a href="http://forum.quik.ru/messages/forum10/message81581/topic8563/">Если бы я был архитектором QUIK</a></b> <i> Что стоило бы изменить в QUIK по-крупному</i> в форуме <a href="http://forum.quik.ru/forum10/">Программирование на языке Lua</a>. <br />
			<br />====quote====<br /><a class="blog-p-user-name" id="bp_H4dkmv01" href="/user/13952/" bx-tooltip-user-id="13952">TGB</a> написал:<br />полуинтеллектуальный робот-спамер<br />=============<br />Полуробот, скорее.<br /><br />====quote====<br /><a class="blog-p-user-name" id="bp_BPWAGxKN" href="/user/13952/" bx-tooltip-user-id="13952">TGB</a> написал:<br />без способности учета контекста обсуждаемой темы при генерации спама.<br />=============<br />У него памяти не хватает, чтобы загрузить весь контекст в память. Вон весь форум загадил кучей новых тем про нехватку памяти. <br />
			<i>20.02.2026 14:59:22, Йцукен.</i>]]></description>
			<link>http://forum.quik.ru/messages/forum10/message81581/topic8563/</link>
			<guid>http://forum.quik.ru/messages/forum10/message81581/topic8563/</guid>
			<pubDate>Fri, 20 Feb 2026 14:59:22 +0300</pubDate>
			<category>Программирование на языке Lua</category>
		</item>
		<item>
			<title>Если бы я был архитектором QUIK</title>
			<description><![CDATA[<b><a href="http://forum.quik.ru/messages/forum10/message81579/topic8563/">Если бы я был архитектором QUIK</a></b> <i> Что стоило бы изменить в QUIK по-крупному</i> в форуме <a href="http://forum.quik.ru/forum10/">Программирование на языке Lua</a>. <br />
			<br />====quote====<br /><a class="blog-p-user-name" id="bp_61P7XCWb" href="/user/62/" bx-tooltip-user-id="62">nikolz</a> написал:<br />Я специально выложил для начинающих писателей не псевдокод, а скрипт на Lua с очередью. Да и в документации на QLua есть еще один пример.<br />=============<br /> &nbsp; Какой же вы непонятливый. До сих пор не поняли, что обсуждаются не существующие реализации, а вариант изменения этих реализаций.<br /> &nbsp; Все таки, похоже, что вы полуинтеллектуальный робот-спамер устаревшей версии, без способности учета контекста обсуждаемой темы при генерации спама. <br />
			<i>20.02.2026 14:48:52, TGB.</i>]]></description>
			<link>http://forum.quik.ru/messages/forum10/message81579/topic8563/</link>
			<guid>http://forum.quik.ru/messages/forum10/message81579/topic8563/</guid>
			<pubDate>Fri, 20 Feb 2026 14:48:52 +0300</pubDate>
			<category>Программирование на языке Lua</category>
		</item>
		<item>
			<title>Если бы я был архитектором QUIK</title>
			<description><![CDATA[<b><a href="http://forum.quik.ru/messages/forum10/message81578/topic8563/">Если бы я был архитектором QUIK</a></b> <i> Что стоило бы изменить в QUIK по-крупному</i> в форуме <a href="http://forum.quik.ru/forum10/">Программирование на языке Lua</a>. <br />
			<br />====quote====<br /><a class="blog-p-user-name" id="bp_eiqmEKNx" href="/user/17142/" bx-tooltip-user-id="17142">Йцукен</a> написал:<br /><noindex><a href="/user/13952/" target="_blank" rel="nofollow">TGB</a></noindex>, можете написать прсевдокод, как по-вашему, использовать очередь событий?<br />=============<br />Вы не читаете форум. <br />Я специально выложил для начинающих писателей не псевдокод, а скрипт на Lua с очередью. Да и в документации на QLua есть еще один пример.<br />--------------------<br />Ах, ошибся, Вы же не писатель, Вы же ПОЭТ. <br />
			<i>20.02.2026 14:23:41, nikolz.</i>]]></description>
			<link>http://forum.quik.ru/messages/forum10/message81578/topic8563/</link>
			<guid>http://forum.quik.ru/messages/forum10/message81578/topic8563/</guid>
			<pubDate>Fri, 20 Feb 2026 14:23:41 +0300</pubDate>
			<category>Программирование на языке Lua</category>
		</item>
		<item>
			<title>Если бы я был архитектором QUIK</title>
			<description><![CDATA[<b><a href="http://forum.quik.ru/messages/forum10/message81577/topic8563/">Если бы я был архитектором QUIK</a></b> <i> Что стоило бы изменить в QUIK по-крупному</i> в форуме <a href="http://forum.quik.ru/forum10/">Программирование на языке Lua</a>. <br />
			<br />====quote====<br /><a class="blog-p-user-name" id="bp_eKx6dYYL" href="/user/62/" bx-tooltip-user-id="62">nikolz</a> написал:<br />все что делается внутри sleep - это передается ядру время на которое надо остановить поток.<br />=============<br /> &nbsp; Вы, как дятел <img src="http://forum.quik.ru/upload/main/smiles/5/icon_smile.png" border="0" data-code=":smile:" data-definition="SD" alt=":smile:" style="width:16px;height:16px;" title="С улыбкой" class="bx-smile" /> , про свое, не относящееся к обсуждаемому.<br /> &nbsp; Вы это читали ?:<br /><br />====quote====<br /><a class="blog-p-user-name" id="bp_fUcEpAxb" href="/user/13952/" bx-tooltip-user-id="13952">TGB</a> написал:<br />-- Отличие только в реализации функции sleep.<br />-- При &nbsp;ее вызове с заданным интервалом внутри нее выполняется (код на C++):<br /> &nbsp; &nbsp;WaitForSingleObject( , &nbsp;); // здесь ожидание истечения &nbsp; или выдачи сигнала QUIK (неблокирующей функцией Pulse в служебном потоке) о записи в очереди новых событий.<br /> &nbsp;-- Когда sleep "срывается" с &nbsp;, то в ней анализируются очереди (это &nbsp;можно сделать эффективно, используя битовую шкалу непустых очередей скрипта) и выполняются соответствующие &nbsp; с параметрами считанными из очередей.<br /> &nbsp;-- Функционально это не отличается от того, что реализовано сейчас, но выполняется в потоке пользователя main.<br /> <br />=============<br /> <br />
			<i>20.02.2026 14:21:42, TGB.</i>]]></description>
			<link>http://forum.quik.ru/messages/forum10/message81577/topic8563/</link>
			<guid>http://forum.quik.ru/messages/forum10/message81577/topic8563/</guid>
			<pubDate>Fri, 20 Feb 2026 14:21:42 +0300</pubDate>
			<category>Программирование на языке Lua</category>
		</item>
		<item>
			<title>Если бы я был архитектором QUIK</title>
			<description><![CDATA[<b><a href="http://forum.quik.ru/messages/forum10/message81576/topic8563/">Если бы я был архитектором QUIK</a></b> <i> Что стоило бы изменить в QUIK по-крупному</i> в форуме <a href="http://forum.quik.ru/forum10/">Программирование на языке Lua</a>. <br />
			<br />====quote====<br /><a class="blog-p-user-name" id="bp_2cz4EkfM" href="/user/17142/" bx-tooltip-user-id="17142">Йцукен</a> написал:<br /> <br />====quote====<br />В любой теме, где есть обсуждение,<br />Он уже оставил свой след.<br />Не вникая в суть предложения,<br />Он вещает свой «важный» бред.<br />=============<br /> <br />=============<br />Прекрасно, Наконец-то вы занялись самокритикой.<br />Сами сочинили или опять плагиат? <br />
			<i>20.02.2026 14:20:29, nikolz.</i>]]></description>
			<link>http://forum.quik.ru/messages/forum10/message81576/topic8563/</link>
			<guid>http://forum.quik.ru/messages/forum10/message81576/topic8563/</guid>
			<pubDate>Fri, 20 Feb 2026 14:20:29 +0300</pubDate>
			<category>Программирование на языке Lua</category>
		</item>
		<item>
			<title>Если бы я был архитектором QUIK</title>
			<description><![CDATA[<b><a href="http://forum.quik.ru/messages/forum10/message81575/topic8563/">Если бы я был архитектором QUIK</a></b> <i> Что стоило бы изменить в QUIK по-крупному</i> в форуме <a href="http://forum.quik.ru/forum10/">Программирование на языке Lua</a>. <br />
			<br />====quote====<br />В любой теме, где есть обсуждение,<br />Он уже оставил свой след.<br />Не вникая в суть предложения,<br />Он вещает свой «важный» бред.<br />=============<br /> <br />
			<i>20.02.2026 14:18:03, Йцукен.</i>]]></description>
			<link>http://forum.quik.ru/messages/forum10/message81575/topic8563/</link>
			<guid>http://forum.quik.ru/messages/forum10/message81575/topic8563/</guid>
			<pubDate>Fri, 20 Feb 2026 14:18:03 +0300</pubDate>
			<category>Программирование на языке Lua</category>
		</item>
		<item>
			<title>Если бы я был архитектором QUIK</title>
			<description><![CDATA[<b><a href="http://forum.quik.ru/messages/forum10/message81573/topic8563/">Если бы я был архитектором QUIK</a></b> <i> Что стоило бы изменить в QUIK по-крупному</i> в форуме <a href="http://forum.quik.ru/forum10/">Программирование на языке Lua</a>. <br />
			все что делается внутри sleep - это передается ядру время на которое надо остановить поток. Ядро настраивает таймер на событие -разбудить поток через надцать секунд и передает управление следующему в очереди потоку. <br />
			<i>20.02.2026 13:44:29, nikolz.</i>]]></description>
			<link>http://forum.quik.ru/messages/forum10/message81573/topic8563/</link>
			<guid>http://forum.quik.ru/messages/forum10/message81573/topic8563/</guid>
			<pubDate>Fri, 20 Feb 2026 13:44:29 +0300</pubDate>
			<category>Программирование на языке Lua</category>
		</item>
		<item>
			<title>Если бы я был архитектором QUIK</title>
			<description><![CDATA[<b><a href="http://forum.quik.ru/messages/forum10/message81572/topic8563/">Если бы я был архитектором QUIK</a></b> <i> Что стоило бы изменить в QUIK по-крупному</i> в форуме <a href="http://forum.quik.ru/forum10/">Программирование на языке Lua</a>. <br />
			<br />====quote====<br /><a class="blog-p-user-name" id="bp_av3uoD1X" href="/user/13952/" bx-tooltip-user-id="13952">TGB</a> написал:<br /> <br />====quote====<br /><noindex><a href="/user/62/" target="_blank" rel="nofollow">nikolz</a></noindex> написал:<br />Sleep останавливает поток main, а функции колбеков вызываются в другом потоке, на который sleep не действует.<br />=============<br /> &nbsp; &nbsp; Вы опять только пишите не читая.<br /> &nbsp; &nbsp;Мы же с Йцукен обсуждаем не системный Sleep и даже sleep QLua, а предложенный мной запускающий коллбеки.<br />Вам опять надо проложиться между комментариев :: ?<br />=============<br />Что вы так переживаете?<br />Я написал пояснение о работе sleep в потоках. <br />Так как г-н <noindex><a href="https://forum.quik.ru/user/17142/" target="_blank" rel="nofollow">Йцукен</a></noindex> нефига в этом не понимает, а Вы ему ( я прочитал Ваш ответ) <br /><br />====quote====<br /><a class="blog-p-user-name" id="bp_hZ2b5tle" href="/user/13952/" bx-tooltip-user-id="13952">TGB</a> написал:<br /> <br />====quote====<br />Да, так называемые коллбеки, должны вызываться внутри sleep (выполняемой в потоке main) с данными из очередей.<br />=============<br /><br />=============<br />Это полная чушь.<br />Ничего внутри sleep не вызывается т к функция sleep выполняется ядром OS. &nbsp;В это время никакой колбек не успеет ничего сделать.<br />-------------------<br /> &nbsp; <br />
			<i>20.02.2026 13:40:28, nikolz.</i>]]></description>
			<link>http://forum.quik.ru/messages/forum10/message81572/topic8563/</link>
			<guid>http://forum.quik.ru/messages/forum10/message81572/topic8563/</guid>
			<pubDate>Fri, 20 Feb 2026 13:40:28 +0300</pubDate>
			<category>Программирование на языке Lua</category>
		</item>
		<item>
			<title>Если бы я был архитектором QUIK</title>
			<description><![CDATA[<b><a href="http://forum.quik.ru/messages/forum10/message81570/topic8563/">Если бы я был архитектором QUIK</a></b> <i> Что стоило бы изменить в QUIK по-крупному</i> в форуме <a href="http://forum.quik.ru/forum10/">Программирование на языке Lua</a>. <br />
			<br />====quote====<br /><a class="blog-p-user-name" id="bp_fflFen89" href="/user/13952/" bx-tooltip-user-id="13952">TGB</a> написал:<br />Вы опять только пишите не читая.<br />=============<br />А зачем, если можно высказать своё "важное" мнение, прочитав только последний комментарий? <br />
			<i>20.02.2026 13:23:07, Йцукен.</i>]]></description>
			<link>http://forum.quik.ru/messages/forum10/message81570/topic8563/</link>
			<guid>http://forum.quik.ru/messages/forum10/message81570/topic8563/</guid>
			<pubDate>Fri, 20 Feb 2026 13:23:07 +0300</pubDate>
			<category>Программирование на языке Lua</category>
		</item>
		<item>
			<title>Если бы я был архитектором QUIK</title>
			<description><![CDATA[<b><a href="http://forum.quik.ru/messages/forum10/message81568/topic8563/">Если бы я был архитектором QUIK</a></b> <i> Что стоило бы изменить в QUIK по-крупному</i> в форуме <a href="http://forum.quik.ru/forum10/">Программирование на языке Lua</a>. <br />
			<br />====quote====<br /><a class="blog-p-user-name" id="bp_pYpjRQ7h" href="/user/62/" bx-tooltip-user-id="62">nikolz</a> написал:<br />Sleep останавливает поток main, а функции колбеков вызываются в другом потоке, на который sleep не действует.<br />=============<br /> &nbsp; &nbsp;Вы опять только пишите не читая.<br /> &nbsp; &nbsp;Мы же с Йцукен обсуждаем не системный Sleep и даже sleep QLua, а предложенный мной запускающий коллбеки.<br />Вам опять надо проложиться между комментариев <img src="http://forum.quik.ru/upload/main/smiles/5/icon_smile.png" border="0" data-code=":smile:" data-definition="SD" alt=":smile:" style="width:16px;height:16px;" title="С улыбкой" class="bx-smile" /> ? <br />
			<i>20.02.2026 13:00:33, TGB.</i>]]></description>
			<link>http://forum.quik.ru/messages/forum10/message81568/topic8563/</link>
			<guid>http://forum.quik.ru/messages/forum10/message81568/topic8563/</guid>
			<pubDate>Fri, 20 Feb 2026 13:00:33 +0300</pubDate>
			<category>Программирование на языке Lua</category>
		</item>
		<item>
			<title>Если бы я был архитектором QUIK</title>
			<description><![CDATA[<b><a href="http://forum.quik.ru/messages/forum10/message81565/topic8563/">Если бы я был архитектором QUIK</a></b> <i> Что стоило бы изменить в QUIK по-крупному</i> в форуме <a href="http://forum.quik.ru/forum10/">Программирование на языке Lua</a>. <br />
			<br />====quote====<br /><a class="blog-p-user-name" id="bp_iZIEZcqW" href="/user/17142/" bx-tooltip-user-id="17142">Йцукен</a> написал:<br /> колбэки внутри sleep должны вызываться или как?<br />=============<br />Sleep останавливает поток main, <br />а функции колбеков вызываются в другом потоке, на который sleep не действует. <br />
			<i>20.02.2026 12:50:07, nikolz.</i>]]></description>
			<link>http://forum.quik.ru/messages/forum10/message81565/topic8563/</link>
			<guid>http://forum.quik.ru/messages/forum10/message81565/topic8563/</guid>
			<pubDate>Fri, 20 Feb 2026 12:50:07 +0300</pubDate>
			<category>Программирование на языке Lua</category>
		</item>
		<item>
			<title>Если бы я был архитектором QUIK</title>
			<description><![CDATA[<b><a href="http://forum.quik.ru/messages/forum10/message81564/topic8563/">Если бы я был архитектором QUIK</a></b> <i> Что стоило бы изменить в QUIK по-крупному</i> в форуме <a href="http://forum.quik.ru/forum10/">Программирование на языке Lua</a>. <br />
			<br />====quote====<br /><a class="blog-p-user-name" id="bp_bvATodD1" href="/user/17142/" bx-tooltip-user-id="17142">Йцукен</a> написал:<br />А как вы узнаете, что это повторы, пока не обработаете их?<br />=============<br /> &nbsp; &nbsp;Вы сначала создаете таблицу фильтрации со значениями полей фильтрации заведомо не совпадающими с ожидаемыми данными. <br />
			<i>20.02.2026 12:35:25, TGB.</i>]]></description>
			<link>http://forum.quik.ru/messages/forum10/message81564/topic8563/</link>
			<guid>http://forum.quik.ru/messages/forum10/message81564/topic8563/</guid>
			<pubDate>Fri, 20 Feb 2026 12:35:25 +0300</pubDate>
			<category>Программирование на языке Lua</category>
		</item>
		<item>
			<title>Если бы я был архитектором QUIK</title>
			<description><![CDATA[<b><a href="http://forum.quik.ru/messages/forum10/message81560/topic8563/">Если бы я был архитектором QUIK</a></b> <i> Что стоило бы изменить в QUIK по-крупному</i> в форуме <a href="http://forum.quik.ru/forum10/">Программирование на языке Lua</a>. <br />
			<br />====quote====<br /><a class="blog-p-user-name" id="bp_2NNjSICl" href="/user/13952/" bx-tooltip-user-id="13952">TGB</a> написал:<br />Если вы не хотите повторно обрабатывать данные, то фильтруйте их.<br />=============<br />А как вы узнаете, что это повторы, пока не обработаете их?<br />====quote====<br /><a class="blog-p-user-name" id="bp_vpgCDXXu" href="/user/13952/" bx-tooltip-user-id="13952">TGB</a> написал:<br />Для фильтрации данных можно создать, для соответствующего вида коллбека, таблицу с фильтруемыми полями, обновляемыми при поступлении новых данных и использовать эту таблицу в начале коллбека с тем. чтобы не обрабатывать ненужные вам повторы.<br />=============<br />Вам надо будет сначала, как минимум запросить данные, чтобы сравнить с предыдущими. <br />
			<i>20.02.2026 12:09:03, Йцукен.</i>]]></description>
			<link>http://forum.quik.ru/messages/forum10/message81560/topic8563/</link>
			<guid>http://forum.quik.ru/messages/forum10/message81560/topic8563/</guid>
			<pubDate>Fri, 20 Feb 2026 12:09:03 +0300</pubDate>
			<category>Программирование на языке Lua</category>
		</item>
		<item>
			<title>Если бы я был архитектором QUIK</title>
			<description><![CDATA[<b><a href="http://forum.quik.ru/messages/forum10/message81558/topic8563/">Если бы я был архитектором QUIK</a></b> <i> Что стоило бы изменить в QUIK по-крупному</i> в форуме <a href="http://forum.quik.ru/forum10/">Программирование на языке Lua</a>. <br />
			<br />====quote====<br /><a class="blog-p-user-name" id="bp_81tKQH1a" href="/user/17142/" bx-tooltip-user-id="17142">Йцукен</a> написал:<br />А смысл несколько раз подряд обрабатывать, например OnQuote, если каждый раз мы будем обрабатывать одни и те же данные?Или вы предлагаете хранить данные для каждого колбэка?<br />=============<br /> &nbsp;Если вы не хотите повторно обрабатывать данные, то фильтруйте их. Для фильтрации данных можно создать, для соответствующего вида коллбека, таблицу с фильтруемыми полями, обновляемыми при поступлении новых данных и использовать эту таблицу в начале коллбека с тем. чтобы не обрабатывать ненужные вам повторы. <br />
			<i>20.02.2026 11:49:34, TGB.</i>]]></description>
			<link>http://forum.quik.ru/messages/forum10/message81558/topic8563/</link>
			<guid>http://forum.quik.ru/messages/forum10/message81558/topic8563/</guid>
			<pubDate>Fri, 20 Feb 2026 11:49:34 +0300</pubDate>
			<category>Программирование на языке Lua</category>
		</item>
		<item>
			<title>Если бы я был архитектором QUIK</title>
			<description><![CDATA[<b><a href="http://forum.quik.ru/messages/forum10/message81556/topic8563/">Если бы я был архитектором QUIK</a></b> <i> Что стоило бы изменить в QUIK по-крупному</i> в форуме <a href="http://forum.quik.ru/forum10/">Программирование на языке Lua</a>. <br />
			А смысл несколько раз подряд обрабатывать, например OnQuote, если каждый раз мы будем обрабатывать одни и те же данные?<br />Или вы предлагаете хранить данные для каждого колбэка? <br />
			<i>20.02.2026 11:24:39, Йцукен.</i>]]></description>
			<link>http://forum.quik.ru/messages/forum10/message81556/topic8563/</link>
			<guid>http://forum.quik.ru/messages/forum10/message81556/topic8563/</guid>
			<pubDate>Fri, 20 Feb 2026 11:24:39 +0300</pubDate>
			<category>Программирование на языке Lua</category>
		</item>
		<item>
			<title>Если бы я был архитектором QUIK</title>
			<description><![CDATA[<b><a href="http://forum.quik.ru/messages/forum10/message81554/topic8563/">Если бы я был архитектором QUIK</a></b> <i> Что стоило бы изменить в QUIK по-крупному</i> в форуме <a href="http://forum.quik.ru/forum10/">Программирование на языке Lua</a>. <br />
			<br />====quote====<br /><a class="blog-p-user-name" id="bp_6PUisQk5" href="/user/17142/" bx-tooltip-user-id="17142">Йцукен</a> написал:<br />а что делать, если между слипами данные поступили по инструменту несколько раз?<br />=============<br /> &nbsp; При "просыпании" sleep, функции чтения очередей (коллбеки) будут выполняться (последовательно, без пропусков) столько раз сколько необработанных записей в очередях. Если очереди не переполняются, а это можно контролировать и не допускать, то события не теряются, но возможны задержки в их отработке. <br /> &nbsp; Как было указано ранее, sleep "просыпается" либо по времени, либо по записи в очереди данных из QUIK. Но при этом в в sleep всегда анализируются и при необходимости обрабатываются непустые служебные очереди скрипта. <br />
			<i>20.02.2026 11:01:47, TGB.</i>]]></description>
			<link>http://forum.quik.ru/messages/forum10/message81554/topic8563/</link>
			<guid>http://forum.quik.ru/messages/forum10/message81554/topic8563/</guid>
			<pubDate>Fri, 20 Feb 2026 11:01:47 +0300</pubDate>
			<category>Программирование на языке Lua</category>
		</item>
		<item>
			<title>Если бы я был архитектором QUIK</title>
			<description><![CDATA[<b><a href="http://forum.quik.ru/messages/forum10/message81550/topic8563/">Если бы я был архитектором QUIK</a></b> <i> Что стоило бы изменить в QUIK по-крупному</i> в форуме <a href="http://forum.quik.ru/forum10/">Программирование на языке Lua</a>. <br />
			<a class="blog-p-user-name" id="bp_6HWb0rkj" href="/user/13952/" bx-tooltip-user-id="13952">TGB</a>, а что делать, если между слипами данные поступили по инструменту несколько раз? <br />
			<i>20.02.2026 10:03:39, Йцукен.</i>]]></description>
			<link>http://forum.quik.ru/messages/forum10/message81550/topic8563/</link>
			<guid>http://forum.quik.ru/messages/forum10/message81550/topic8563/</guid>
			<pubDate>Fri, 20 Feb 2026 10:03:39 +0300</pubDate>
			<category>Программирование на языке Lua</category>
		</item>
		<item>
			<title>Если бы я был архитектором QUIK</title>
			<description><![CDATA[<b><a href="http://forum.quik.ru/messages/forum10/message81536/topic8563/">Если бы я был архитектором QUIK</a></b> <i> Что стоило бы изменить в QUIK по-крупному</i> в форуме <a href="http://forum.quik.ru/forum10/">Программирование на языке Lua</a>. <br />
			<br />====quote====<br /><a class="blog-p-user-name" id="bp_53KVyQEa" href="/user/17142/" bx-tooltip-user-id="17142">Йцукен</a> написал:<br />колбэки внутри sleep должны вызываться или как?<br />=============<br /> &nbsp; Да, так называемые коллбеки, должны вызываться внутри sleep (выполняемой в потоке main) с данными из очередей. <br />
			<i>19.02.2026 16:00:27, TGB.</i>]]></description>
			<link>http://forum.quik.ru/messages/forum10/message81536/topic8563/</link>
			<guid>http://forum.quik.ru/messages/forum10/message81536/topic8563/</guid>
			<pubDate>Thu, 19 Feb 2026 16:00:27 +0300</pubDate>
			<category>Программирование на языке Lua</category>
		</item>
	</channel>
</rss>
