200 / 87 / 9
Регистрация: 15.11.2010
Сообщений: 472
|
|
1 | |
Цикл, обрабатывающий события от нескольких источников25.03.2017, 02:32. Показов 1971. Ответов 15
Метки нет (Все метки)
Здравствуйте, форумчане!
Помогите, пожалуйста, решить такую проблему, которая меня заинтересовала. Требуется создать цикл обработки событий или сообщений от двух или более источников. Т. е., допустим, имеется несколько (более одного) источника событий, каждый из них порождённые им события помещает в свою очередь, причём эти очереди разные, общей единой очереди событий, в которую все источники делали бы запись, не существует. События могут считываться из очередей любым возможным способом — могут использоваться для этого системные вызовы операционной системы (допустим, read/write в Unix и Linux), мьютексы и блокировки чтения/записи, какие-то специальные библиотечные функции и т. п. Источники событий могут быть любые — пайпы, FIFO, сокеты, X Server, посылающий клиентской программе события клавиатуры и мыши, и т. п. Главное, чтобы их было несколько, при этом они могут быть и однотипными, и совершенно разной природы. Работать цикл должен так. При поступлении события с любого из источников он должен его считывать, обрабатывать и выполнять какое-то положенное ответное действие. В случае же если событий ни на одном из источников нет (все прежние события уже считаны и обработаны циклом, а новых ещё не поступило), цикл должен блокироваться и останавливаться в ожидании нового события. Как только событие поступило, не важно с какого из источников, цикл должен разблокироваться и обрабатывать его. В случае одного источника событий задача тривиальна и не вызывает никаких вопросов. А как быть, если их несколько? Какие существуют решения и подходы?
0
|
25.03.2017, 02:32 | |
Ответы с готовыми решениями:
15
Построить цикл, обрабатывающий строки матрицы Отчет из нескольких источников данных Слияние в Word из нескольких источников Сбор данных из нескольких источников |
200 / 87 / 9
Регистрация: 15.11.2010
Сообщений: 472
|
|
25.03.2017, 08:27 [ТС] | 3 |
nimazzzy, спасибо за ответ. Я не знал о существовании этих системных вызовов, а поэтому у меня родилось такое решение.
Для каждого источника создать свой отдельный поток (pthread), а в нём запустить свой маленький простенький цикл обработки событий именно для этого источника. При наступлении события функция или системный вызов, считывающий событие от данного источника и находящийся в этом цикле, разблокировался бы и освобождал бы некоторый мьютекс, общий для всех потоков. Помимо потоков с циклами обработки событий для каждого источника существовал бы ещё один — главный поток, со своим циклом обработки сообщений, который обрабатывал бы все события ото всех источников. Цикл обработки событий в этом потоке должен был бы удерживаться в заблокированном состоянии тем самым мьютексом, о котором я только что говорил, а при разблокировании этого мьютекса любым из потоков, обрабатывающих индивидуальные источники, разблокировался бы и цикл головного потока, обрабатывающий все события. Потом, естественно, после того, как им все события обработаны, головной цикл бы блокировал мьютекс, после чего сам бы засыпал. А дальше задача по очередной разблокировке мьютекса (и пробуждению головного потока) ложилась бы на эти второстепенные потоки. Интересно, что вы думаете о такой схеме. Мне кажется, она вполне работоспособна, но неоправданно сложна для такой задачи. Городить для каждого источника сообщений свою отдельную нить (thread) для такой элементарной задачи излишество. В конце концов, потоки в Unix появились довольно поздно, а описанная мной задача наверняка существовала гораздо раньше и была решена другими более простыми и изящными средствами.
0
|
Заблокирован
|
|
25.03.2017, 16:53 | 4 |
Тогда не нужны вызовы для проверки дескрипторов. Каждый поток и так будет висеть на чтении.
А почему эту "обработку" тогда каждый поток не может сам сделать?
Я думаю, что надо исходить из задачи. А ты начинаешь с решения какой-то абстрактной сущности с какими-то абстрактными обработками. Поэтому я тут никак не думаю, так как у меня нет ТЗ.
1
|
200 / 87 / 9
Регистрация: 15.11.2010
Сообщений: 472
|
|
25.03.2017, 21:13 [ТС] | 5 |
Всё правильно — эту обработку мог бы сделать и каждый поток по отдельности. Просто я не знал о существовании вызовов вроде select и poll, а хотел организовать всё-таки обработку событий от разных источников в одном потоке, в одном цикле обработки сообщений. Поэтому мне и пришлось по сути дела имитировать работу этих системных вызовов, создавая для каждого источника свой висящий на нём поток. А мьютекс, который бы эти потоки, висящие на источниках, освобождали и через который они бы управляли головным потоком с главным циклом обработки сообщений, как раз бы играл роль системного вызова select или poll. Вместо блокирующего системного вызова select или poll главный поток блокировался бы в цикле, допустим, на вызове pthread_mutex_lock.
Т. е. я не знал вызовов select и poll, а хотел симитировать подобное поведение известными мне средствами и сделать основную обработку всё-таки в одном потоке. Такая задача у меня есть, пусть и в довольно расплывчатой формулировке. Чуть позже о ней напишу.
0
|
200 / 87 / 9
Регистрация: 15.11.2010
Сообщений: 472
|
|
26.03.2017, 01:05 [ТС] | 7 |
А вот и сама задача.
Имеется мысль написать некоторую интерфейсную иксовую GUI программу, которая бы выводила некоторую информацию, рисовала таблицы, показывала рисунки и графики. При этом программа, с одной стороны бы использовала ввод от пользователя, т. е. события X Server'а от мыши и клавиатуры (она бы управлялась пользователем, как любая GUI-программа), а с другой стороны использовала бы какой-то механизм IPC (например, FIFO) в качестве источника внешних данных от других программ. Допустим, через FIFO ей бы поставлялись данные с датчиков каких-то приборов. К примеру, мы могли бы написать систему управления каким-то устройством и нас бы интересовали его параметры — температура, напряжение, ток, частота вращения двигателя и т. п. (хотя это всего лишь один из многих примеров). Т. е. программа бы использовала два источника информации: 1. Пользовательский ввод — события клавиатуры и мыши. 2. Внешние входные данные, поставляемые ей через FIFO (или какой-либо иной механизм IPC). И тут возникает вопрос, как данные, поступающие от этих двух источников, обрабатывать. В целом, логично их обрабатывать в цикле. Но тут можно поступить двояко. Для каждого из двух источников — X Server'а и FIFO — можно создать свой цикл обработки событий и запустить каждый цикл в своём потоке. Тогда события от обоих источников будут обрабатываться параллельно и независимо, а при необходимости (когда между ними будут возникать какие-то противоречия) потоки будут синхронизировать друг друга через мьютексы и rw_lock'и. Можно поступить иначе. Можно использовать единый общий цикл обработки событий, который будет фиксировать появление данных, поступающих как от X Server'а, так и от FIFO, и обрабатывать их. При этом, несмотря на два различных источника данных, цикл обработки данных будет один и работать он будет в одном потоке. Но тут возникает проблема — блокироваться и разблокироваться цикл должен на какой-то функции, регистрирующей наличие или отсутствие данных не в одном, а в двух источниках. При отсутствии данных в обоих источниках функция должна становиться в ожидание, а при наличии данных хотя бы в одном из источников функция должна "открываться". Вот и встаёт вопрос, как в такой системе, использующей с одной стороны X Server, а с другой стороны FIFO, сразу проверить состояние данных в обоих источниках и при появлении их хотя бы в одном источнике разблокироваться. Работа с иксами может производиться через любую библиотеку — Xlib, GTK+, QT, причём Xlib для меня сейчас наиболее предпочтительный вариант. Как известно, все эти библиотеки для считывания событий оконной системы используют какую-нибудь функцию, например Xlib использует функцию XNextEvent(). Вот и возникает вопрос, как, имея с одной стороны файл FIFO, а с другой стороны функции чтения событий из очереди вроде XNextEvent(), сделать проверку наличия данных и там, и там, и организовать общий "сводный" цикл, обрабатывающий события и из того, и из другого источника. А так, мне кажется, что для решения данной задачи имеют право на жизнь оба подхода. Можно сделать для каждого источника свой независимый поток, а в нём устроить свой цикл обработки событий. А можно программу сделать однопоточной с единым циклом обработки событий, обрабатывающим как оконные события, так и поступление данных из FIFO. Но тогда возникает проблема определения состояния обоих источников сразу и блокировки/разблокировки на них. Как её здесь решить, я не знаю. Добавлено через 22 минуты P.S. Пишу так гуманитарно и многословно потому что планы и представления, как это всё должно работать, очень расплывчаты пока. Да и похожих вещей не делал раньше, с подобными проблемами сталкиваюсь впервые. Добавлено через 1 час 28 минут nimazzzy, какие у тебя мысли есть?
0
|
Заблокирован
|
|
26.03.2017, 23:39 | 8 |
Сообщение было отмечено JohnyWalker как решение
Решение
С xlib не работал, но, учитывая архитектуру иксов (клиент-сервер на сокетах), там можно и в одном потоке сделать с select'ом. Используя ConnectionNumber получить дескриптор сокета и вперед. Но, если обработка данных перед отображением - штука затратная, то я бы разделил на потоки.
1
|
200 / 87 / 9
Регистрация: 15.11.2010
Сообщений: 472
|
|
27.03.2017, 03:52 [ТС] | 9 |
nimazzzy, спасибо тебе за ответ. Что интересно, такое же решение этой проблемы предлагают и на англоязычных форумах вроде stackoverflow. Вот ссылки с примерами кода, вдруг кому-нибудь это будет интересно.
http://stackoverflow.com/quest... xnextevent http://stackoverflow.com/quest... chronously http://www.linuxquestions.org/... ?p=2431345 — здесь наиболее подробное обсуждение и больше всего примеров исходников, поэтому эту ссылку наверное стоит рекомендовать в первую очередь http://stackoverflow.com/quest... er-process http://stackoverflow.com/quest... ent-occurs Добавлено через 12 минут nimazzzy, ещё такой вопрос. Мне никогда ранее ни с select(), ни с poll()/epoll() не приходилось работать. Где обо всём этом (ну, прежде всего, о select()) почитать? Книжка "Стивенс, Раго" сойдёт?
0
|
200 / 87 / 9
Регистрация: 15.11.2010
Сообщений: 472
|
|
27.03.2017, 20:00 [ТС] | 11 |
nimazzzy и другие, напоследок такой вопрос. Для общего развития.
А как моя проблема (случай, когда поступающие события надо обрабатывать не только от оконной системы, но и от какого-то другого источника и чтобы чтение событий от оконной системы не блокировало события от второго источника) решается не в xlib, а средствами GTK+ и QT? Тоже нужно получить дескриптор сокета иксов и использовать вызов select()? Или там, может, для решения этой задачи существуют свои специальные средства, специфичные именно для этих библиотек? Добавлено через 2 часа 5 минут И всё-таки мой вопрос по QT и GTK+. Он для меня так остро сейчас не стоит, но всё же... Может, есть у кого-нибудь какие-то мысли и соображения?
0
|
28.03.2017, 08:59 | 12 |
JohnyWalker, вам нужен не просто цикл. Вам нужна реализация паттерна event queue.
1
|
200 / 87 / 9
Регистрация: 15.11.2010
Сообщений: 472
|
|
28.03.2017, 09:56 [ТС] | 13 |
HighPredator, уточните, пожалуйста, говоря о паттерне event queue, какую библиотеку вы имеете в виду — Xlib, GTK+ или QT? Или вы имеете в виду все их вместе?
И что значит словосочетание "паттерн event queue"? В переводе с английского это означает очередь событий. Понятно, что работая в цикле с сообщениями от любой оконной системы, в т. ч. и с иксами, какую бы библиотеку я не использовал, я в любом случае работаю с очередью событий и выполняю из неё чтение. Но, может быть, в таких кроссплатформенных библиотеках как QT или GTK+ термином "паттерн event queue" обозначается какая-то особая сущность, какая-то специфическая конструкция этой библиотеки, объявленная в ней? Поясните, что вы имели в виду под этим термином, можно и со ссылками на документацию Xlib, GTK+, QT или какой-либо иной библиотеки, которую вы подразумевали.
0
|
28.03.2017, 13:44 | 14 |
Ни одну из них ибо не знаком. Допускаю, что в них есть готовые реализации сего.
В точку. Только ключевая особенность этого паттерна в том, что он позволяет работать с множеством источников разных архитектурно разных(!) событий. Есть две вырожденные (сферические в вакууме) типичные реализации этого паттерна как краевые случаи. Первый это идеальный игровой движок, в котором осуществляется разруливание событий сети, рендеринга, пользовательского ввода, игрового процесса и прочих. Все они разные архитектурно и независимы друг от друга. В этом основная цель паттерна: осуществлять взаимодействие между системами, поддерживая архитектурную развязанность систем друг с другом как и должно быть (decoupling, loose coupling). Второй вырожденный случай, это когда тип, источник и обработчик возникающих событий один. Яркий пример -- простой цикл обработки сообщений в винапи. Да, это не простой паттерн, но и у вас задача соответствующая. Что-то еще вам подрассказать?
0
|
techpriest
634 / 213 / 57
Регистрация: 27.02.2014
Сообщений: 1,180
|
|
28.03.2017, 14:00 | 15 |
Хочу заметить, что реализация с кучей потоков, каждый из которых обрабатывает свой канал и скидывает свой выхлоп в "очередь событий" гораздо мощнее системного select.
0
|
Заблокирован
|
|
28.03.2017, 14:29 | 16 |
Это неправильное утверждение. "Может быть производительнее в некоторых случаях" - это да.
0
|
28.03.2017, 14:29 | |
28.03.2017, 14:29 | |
Помогаю со студенческими работами здесь
16
Чтение по http из нескольких источников Формирование отчета из нескольких источников Поиск из нескольких источников данных Добавить данные в сущность из нескольких источников Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |