LINUX.ORG.RU

Приоритетная очередь

 


0

1

Приветствую

Через однопоточный обработчик поступают mqtt команды и его никак нельзя тормозить ещё и их обработкой, при этом какие то типы команд могут быть быстро обработаны, какие то существенно медленней.

Если с самой очередью приоритотов вроде есть понимание в использовании std::priority_queue (или лучше мудрить с heap???), то как организовать раздельную обработку каждого приоритета, чтобы длинные низкоприоритетные задачи не тормозили более высокие пока не соображу.

Пока только 2 вида, но может в будущем будет больше и не хочется тупо делать количество очередей просто по количеству приоритотов.

На чем такое реализовать на плюсах???

★★★

у тебя сразу возникает проблема: что ты будешь делать, если у тебя прилетела высокоприоритетная команда в общую очередь, а перед ней ещё 100 низкоприоритетных? Выгребать всё на клиента?

Заведи себе две очереди и в своём клиенте сначала смотри в первый приоритет, потом во второй, потом в третий, потом блокируйся на хоть какую-нибудь

max_lapshin ★★★★★
()
Ответ на: комментарий от olelookoe

делаешь адаптивный пул. если нет свободных потоков, а пришло высокоприоритетное сообщение, запускаешь новый поток и добавляешь его в пул. если лишних потоков слишком много (потоки слишком долго спят в ожидании сообщений), потоки удаляются из пула и уничтожаются.

alysnix ★★★
()
Ответ на: комментарий от Irma

Формально обработчик не выполняет никаких вычислений, он незначительно парсит команду, лезет в бд и потом шлёт какой то ответ или добавляет http запрос в уже имеющуюся стандартную очередь, пока основная проблема что некоторые sql команды медленны, т.е. в целом это внешние процессы, а не мои потоки. Тот же mysql может грузить прилично систему и вы не можете заранее знать с каким приоритетом для ядра его выполнять.

Поэтому как видится - каждая более приоритетная задача должна начинать выполняться сразу как поступила, все активные ниже приоритетом после выполнения встают на паузу и ждут исходя из приоритета когда придет их очередь.

wolverin ★★★
() автор топика
Ответ на: комментарий от alex0x08

Приоритет заканчивается в момент начала обработки

зависит от задачи же.

на уровне ручных пауз и продолжений выполнения.

у ядра есть шедулер(ы), руками-то зачем?

при наличии назначенного пула для каждого приоритета за поток из пула будут конкурировать только равноприоритетные сообщения.

сто тысяч медленно выполняющихся потоков куда менее осмысленное явление чем десять быстрых и тыща в ожидании.

да х его з что там за очереди, каких сообщений и как их обрабатывают. может потоки на IO висят, а может там вычисления.

olelookoe ★★★
()
Ответ на: комментарий от olelookoe

Это если сами сообщения отличаются настолько, что обязательно требуют отдельный обработчик.

В случае ТС все же речь только о приоритизации, полагаю у него вообще структурно однинаковые сообщения будут ходить с разным приотитетом обработки и обрабатываться одним и тем же кодом.

alex0x08 ★★★
()
Последнее исправление: alex0x08 (всего исправлений: 1)
Ответ на: комментарий от wolverin

Пока не реализовано, но тем не менее потребуется сейчас так это в определенной обработке на неком этапе запрос-ответ с другого сервера, по ответу дальше сборка данных и пересылка, так что завязаться на приоритет ядра не получится.

wolverin ★★★
() автор топика
Последнее исправление: wolverin (всего исправлений: 1)
Ответ на: комментарий от wolverin

Т.е. самая низкоприоритетная задача выглядит так - пришло в мктт данные ввиде хмл, загрузка их в мускл, затем запрос мктт на другой сервер дополнительных данных, после получения добавление в хмл и отправка на устройство.

Самый высокий - мктт команда + бд запрос + рест запрос (может быть потом ответ о результате последнего)

Это пока текущие, но обычно потом приходится вешать что то ещё на этот функционал

wolverin ★★★
() автор топика
Ответ на: комментарий от wolverin

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

По-сути тебе надо несколько фоновых процессов, которые читают с файла на диске эти твои XML и кладут в базу данных. Все.

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

alex0x08 ★★★
()
Ответ на: комментарий от wolverin

задача выглядит так

ясно-понятно )

тогда забудь всю ту херотень которую коллективный разум лорчика тебе тут понаписал

кури асинхронную работу с БД (и пул соединений).

рест - юзай callback URL.

тогда (как пример работы с твоими высокоприоритетными сообщениями)

  • сообщения ловишь и обрабатываешь в первом потоке, обработал - делаешь асинхронный запрос к БД
  • во втором потоке ловишь ответы от базы и дергаешь рест
  • в третьем потоке ловишь ответы от рест-сервера и чо там еще
olelookoe ★★★
()
Последнее исправление: olelookoe (всего исправлений: 1)

Стартануть N потоков менеджеров под каждый приоритет с определённым системным приоритетом (можно даже слушать на разных портах). Каждый такой поток стартует еще M потоков с нужным системным приоритетом и переиспользует на спинлоке, где M - глубина обработки в очереди. Нужно добавить всякие плюшки вроде таймаутов, а так норм, ведь за приоритет отвечает планировщик системы, его умные люди писали.

ac130kz ★★
()

то как организовать раздельную обработку каждого приоритета, чтобы длинные низкоприоритетные задачи не тормозили более высокие пока не соображу.

В правильно заданном вопросе содержится половина ответа. Поэтому начнем с переформулирования:

Как сделать чтобы высокоприоритетные задачи могли исполняться не ожидая завершения длинной низкоприоритетной задачи?

Т.е. нужна многозадачность в ее общем понимании. Она бывает двух видов: кооперативная и вытесняющая. В первом варианте задачки сами передает управление/поток планировщику, во втором планировщик отбирает управление/поток у задачи. И в этом моменте возникает вилка потому что нет понимая что такое «длинная» задача.

Если «длинная» это про IO (ждем чего-то по сети) то такой задачке не нужен процессор пока этот IO она ждет и можно применить кооперативную многозадачность. Вариант с ограничением количества одновременно запущенных задачек отложим в сторону. Решение асинхронный IO и event-loop. Реализовывать в современном мире такое лучше на короутинах (чит: прототип не обязательно сразу на С++ делать, иногда удобней на каком-нибудь питоне слепить). Работать такая конструкция может на одном треде (сразу избавляемся от ошибок синхронизации как от класса), так и на нескольких, многопоточка тут ортогональна решению. На этом приседания с запуском коротких задач поверх длинных в общем случаи заканчиваются.

Если «длинная» это какие-то числодробительные вычисления, то сама она отдавать поток не будет потому что ей всегда есть что делать и тут уже нужна вытесняющая многозадачность. Сочинять ее заново не разумно когда у нас есть ОС релиазующая такой функционал. Сделано это в виде концепции процессов/потоков: ядер может быть меньше чем потоков/процессов, планировщик ОС дает поработать каждому потоку/процесс по чуть-чуть на свободном ядре. Тут нам нужно переиспользовать то, что уже существует: нам нужны два потока-контекста, один для высокоприоритетных, другой для низкоприоритных задачек. Задачки до потоков-исполнителей нужно доставлять. Приходят они независимо поэтому их нужно складировать в какой-то контейнер(ы) где они будут ждать своей обработки.

Возможен вариант с одним контейнером, который оба потока поедают, возможен вариант с выделенными очередями под каждый поток-контекст.

В первом случаи это будет контейнер-дерево с первым порядком сортировки по приоритету (второй id задачки), поток высокоприоритетных задачек забирает из него только задачки с высоким приоритетом, поток для низкоприоритетных забирает все задачки (а чего ему отдыхать если нет «длинных» задачек, пусть другую работу делает).

Во втором варианте проще - приемник задачек сам разбрасывает их о очередям. Но у него есть недостаток. В реальных проектах нужно контролировать поток запросов/задачек, т.е. прикручивать ограничение длинны очереди и соответственно приостановку писателей в эту очередь. Иначе если задачки приходят быстрее их обработки, очереди съедят всю оперативку и это скорее всего не то поведение которое ожидается. И вот возможна проблема, что заполнится очередь низкоприоритетных задачек и приемник при попытке положить в нее еще упрется на ожидании пока из этой очереди что-то не обработают и в это время не будет отправлять на обработку высокоприоритетные задачи.

dvetutnev
()
Ответ на: комментарий от dvetutnev

все это замечательно, но когда вы делаете запрос SQL - ваша поток с длительной низкоприоритетной задачей ничем не занят, а цпу загружен как минимум по одному ядру на 100% (напомню их только ДВА и моя приблуда не единственная в ОС), поэтому нет как длительных вычислений в моих потоках, так и длительных простоев ЦПУ во время их выполнения - да планируется ожидание по сети низкоприоритетной задачи, сколько это займет пока не реализовано оценить не могу, НО все обработки лезут в локальную БД.

пока исключительно задачу работы с БД переложил на std::priority_queue, сейчас запилю полноценно низкоприоритетную обработку и буду поглядеть по времени, хотя полноценно оценить все равно можно только под реальной нагрузкой.

wolverin ★★★
() автор топика
Ответ на: комментарий от wolverin

но когда вы делаете запрос SQL все обработки лезут в локальную БД.

Выбор реляционной СУБД еще и локальной под задачу быстрой параллельной загрузки данных - откровенно плохая идея.

Скорее всего мяч сейчас вообще не на вашей стороне и вот это:

цпу загружен как минимум по одному ядру на 100%

вам делает сам SQL-сервер а не код с очередями.

Так какая все-таки задача?

Постоянная фоновая обработка данных, не создающая 100% загрузку на систему - это одно. Разово или рывками но с полной нагрузкой на ОС и все доступные ресурсы - это другое.

alex0x08 ★★★
()
Ответ на: комментарий от alex0x08

Нагрузка в целом имеет общий случай - приходит волнами, но задача не нагрузить ОС, а добиться эффективности

МуСКЛ это наследство, он обернут в хттп сервер и используется мобильным приложением (данные суются для него), распределённая куча железок после моего выпиливания почти перестанет его использовать, но останется задача оперативно слать на приложения пуши, а для этого надо ходить в БД и получать токены.

wolverin ★★★
() автор топика
Последнее исправление: wolverin (всего исправлений: 1)
Ответ на: комментарий от wolverin

но останется задача оперативно слать на приложения пуши, а для этого надо ходить в БД и получать токены.

Тогда вот тут проблема, стоит эти токены брать сразу и хранить в памяти, не лазая в базу данных за их получением в момент начала обработки.

Дальше, в реляционной СУБД есть блокировки и индексы, в том числе полнотекстовые. Если начинается цирк под названием массовая вставка, то для того чтобы не трогать индексы и не мешать обычному использованию базы, вставляют во временную таблицу, без индексов а затем в фоне уже из нее раскидывают по нормальным таблицам.

В любом случае, не должно быть записи из большого количества параллельных потоков в базу в одну и ту же таблицу - это рано или поздно привет к блокировке таблицы.

Вообще у меня стойкое ощущение что вы решаете не ту проблему, очередь - это паттерн в программировании, еще и общего назначения. Сам по-себе этот паттерн ничего не ускоряет и никак не помогает.

alex0x08 ★★★
()
Ответ на: комментарий от alex0x08

длинная запись только в низкоприоритетной задаче выполняется, благодаря очереди она однопоточна и не будет блокировки, полагаю основную нагрузку там дает парсинг засунутого в БД хмл, из моей приблуды все остальное это чтение, когда пишут в БД мобильные приложения у тысячи пользователей хз, наверное не часто и тоже в основном чтение.

конечно не ускоряет, пока это дает мне возможность управлять очередностью, потому что повторюсь цель НЕ в ускорении, а в эффективности - заливка хмл в бд может с лагом ну наверное до 1-2 минут стоять на паузе пока отработаютя все пуши - потому что последнее заметно пользователю, а на первое в целом поплевать.

wolverin ★★★
() автор топика
Последнее исправление: wolverin (всего исправлений: 1)
Ответ на: комментарий от wolverin

а в эффективности - заливка хмл в бд может с лагом ну наверное до 1-2 минут стоять на паузе пока отработаютя все пуши

Может тогда не стоит заливать нереляционные данные блобом в реляционную базу? На диске хранить эти XML никак?

потому что последнее заметно пользователю, а на первое в целом поплевать.

Ну ок, те есть какая-то длинная задача, которая выполняется не очень часто, в фоне. И есть быстрые задачи (некие «пуши»), выполнением которых надо управлять. Если они заметны пользователю, полагаю речь о слишком большой загрузке CPU так?

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

Низкий приоритет ставится при создании треда.

alex0x08 ★★★
()
Последнее исправление: alex0x08 (всего исправлений: 2)
Ответ на: комментарий от max_lapshin

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

статистика в проде показала, что низкоприоритетная задача может выполняться от 10 до 60 секунд (avg 30 сек) при этом грузят одно из 2х ядер моего «селерона» на 100%, нагрузочной статистики высокоприоритетных задач у меня нет окромя простой синтетической, не позволяющей создать волну нагрузки, но в целом вроде решение работает в той идеи, что не требуется создавать кучу потоков с кучей подключений к базе по произвольному количеству приоритетов.

зы. да согласен что пулл был бы быстрее, но и в таком варианте пока отклил очень хороший, при этом не гружу на 100% всю систему, не удерживаю кучу ресурсов когда это не нужно, а это имхо и есть эффективность.

wolverin ★★★
() автор топика
Последнее исправление: wolverin (всего исправлений: 3)