Какова аналогия C со структурой событий LabVIEW? - PullRequest
5 голосов
/ 11 марта 2010

Одна программная конструкция, которую я довольно часто использую в LabVIEW, это Структура события . Это дает мне преимущество, заключающееся в том, что мне не нужно тратить циклы ЦП с помощью опроса, а выполнять действия только тогда, когда генерируется интересующее меня событие.

Как опытный программист LabVIEW с приличным пониманием C, мне любопытно, как можно было бы эмулировать структуру событий LabVIEW в C; желательно под Linux. Небольшой пример кода (подобный приведенному в ссылке выше), иллюстрирующий, как это можно сделать, будет очень полезен. Кроме того, если уже существуют сторонние библиотеки (для Linux) для добавления этого фреймворка событий в C, было бы неплохо это знать. Спасибо.

Ответы [ 5 ]

7 голосов
/ 11 марта 2010

Структура событий - это просто абстракция, которая скрывает от вас поток выполнения. Должен быть какой-то код, выполняющийся где-то на компьютере, который проверяет эти события и затем вызывает ваши обработчики событий. в C вы должны будете сами предоставить этот код («основной цикл» программы). Этот код будет проверять различные источники событий, которые вас интересуют, и вызывать функции вашего обработчика событий.

Затем возникает хитрость, заключающаяся в том, чтобы не допустить, чтобы этот основной цикл дико вращал процессор. Один простой трюк состоит в том, чтобы основной цикл находился в спящем режиме в течение определенного периода времени, а затем проверял, нужно ли обрабатывать какие-либо события, и затем снова спал. Это имеет обратную сторону введения задержки. Лучшим трюком, когда это применимо, является то, что операционная система выполняет эти проверки как часть своих обычных операций, а затем запускает основной цикл вашего приложения, когда происходит что-то интересное. В Linux это делается с помощью системного вызова «select», но у select есть ограничение: он может указывать только ресурс, который может быть связан с дескриптором файла, поэтому устройства, stdin, файлы, сетевые порты в порядке.

Редактировать: Чтобы уточнить для моих downvoters: я не отрицаю наличие аппаратных прерываний. Да, в случаях, когда код имеет прямой доступ к аппаратным прерываниям для всех событий, которые он хочет обработать (например, встроенная система или драйвер устройства), вы можете написать действительно «управляемый событиями» код с несколькими точками входа, которые не заняты ожиданием или сном , Однако в обычной программе уровня приложения C, работающей под Linux, эта архитектура кода не существует буквально, а эмулируется на уровне приложения. Любое приложение Linux будет иметь основной цикл и, по крайней мере, один поток выполнения. Этот поток может быть приостановлен планировщиком, но он всегда существует и всегда имеет указатель инструкции на конкретную инструкцию. Если код покидает main (), программа завершается. Для кода нет возможности вернуться из main и позже получить обратный вызов из ядра. Код имеет одну точку входа и должен вручную вызывать различные обработчики событий. За исключением драйвера устройства (или очень специфического системного кода, использующего сигналы), ядро ​​или оборудование не могут автоматически вызывать определенную функцию, если пользователь щелкнул по определенному пункту меню, вместо этого ваш код работает, обнаруживает это событие, и вызывает правильный обработчик события.

Вы можете сказать LabView «Вызовите эту функцию, когда произойдет XX». В C вы говорите свой собственный код отправки события «Вызовите эту функцию, когда произойдет XX».

Что я пытаюсь сказать (плохо?), Так это то, что архитектура среды событий не является родной для приложений C / Linux. Он должен эмулироваться вашим кодом, имея основной поток диспетчеризации, который создает видимость управляемой событиями среды. Либо вы делаете это вручную, либо используете библиотеку событий, которая делает это негласно, чтобы создать видимость модели, управляемой событиями. LabView использует второй подход, поэтому кажется, что никакой код не выполняется, когда не происходит никаких событий, но на самом деле существует собственный код CView LabView, управляющий очередями событий. Это не означает, что он все время ждет, как я уже говорил, есть системные вызовы, такие как select и sleep, которые код может использовать для выдачи времени процессора, когда у него нет работы, но код не может просто прекратить выполнение.

Допустим, вы хотите написать «управляемую событиями» программу с двумя обработчиками событий. Тот, который вызывается каждые десять секунд, называется tick (), а тот, который вызывается при каждом нажатии клавиши, называется key (), а тот, который вызывается каждый раз, когда слово "foobar" набирается, называется foobar (). Вы можете определить эти три обработчика событий, но в дополнение вам нужен некоторый основной поток диспетчеризации, который в основном выполняет

 while not quitting
   If 10 seconds have elapsed, call tick()
   If Key has been Pressed
       call key() 
       add save the key to our key buffer
       If buffer now contains "foobar" call foobar() and clear buffer
   Wait()

Если все события, которые вас волнуют, являются событиями системного уровня или событиями временного уровня, вы можете подождать () может просто сообщить ядру «разбудите меня, когда произойдет одна из этих вещей», поэтому мне не нужно » «Ожидание занято», но вы не можете просто сказать ядру «вызвать foobar (), когда« нажата кнопка foobar ». У вас должен быть код отправки уровня приложения, который эмулирует структуру события. У вашей программы на Си есть только одна точка входа из ядра для каждого потока выполнения. Если вы посмотрите на библиотеки, которые предоставляют модели диспетчеризации событий, такие как Qt, то обнаружите, что они работают так же под капотом.

2 голосов
/ 11 марта 2010

Большинство инструментов GUI ( GTK , Qt и т. Д.) Реализуют собственную абстракцию цикла обработки событий. Я вставил пример программы здесь , потому что это было немного долго, чтобы включить в ответ. Это порт примера LabVIEW, который вы упомянули в C с помощью инструментария GTK, потому что это тот, с которым я знаком. Тем не менее, основы цикла событий не сильно отличаются в других инструментах.

2 голосов
/ 11 марта 2010

Мне нравится libev для такого рода вещей.

0 голосов
/ 10 ноября 2016

Хорошей аналогией со структурой событий LabVIEW является Win32-функция «извлечение события» GetMessage(). GetMessage() ждет вечно, пока не произойдет событие GUI. В Windows гораздо больше событий, даже для каждого дочернего окна (LabVIEW: элемент управления или индикатор), чем в LabVIEW. GetMessage() просто возвращает каждое событие, тонкая фильтрация (как в LabVIEW) должна быть выполнена позже, обычно с использованием DispatchMessage() и процедурой обработчика событий Window WindowProc() с более или менее большим оператором switch().

Большинство получателей используют стиль «события события», который не соответствует структуре события. Программы, управляемые прерываниями.

Если используется тайм-аут, подумайте, что MsgWaitForMultipleObjects() с нулевыми дескрипторами файла вызывается до PeekMessage(). Случай тайм-аута применяется, когда за указанный промежуток времени не произошло ни одного события.

На самом деле, структура событий LabVIEWs должна находиться внутри отдельного цикла. Отдельный цикл - это поток. Для типичного программирования на Win32 GetMessage() используется в основном потоке, а дополнительные ("рабочие") потоки генерируются при взаимодействии с пользователем по мере необходимости.

LabVIEW не может легко создать поток. Это возможно только путем вызова асинхронного SubVI. В самом деле! Поэтому большинство программ LabVIEW используют второй цикл while в качестве постоянно доступного рабочего потока, который будет запускаться, когда что-то должно быть сделано, и блокировать (то есть прекращать потреблять мощность процессора) в противном случае. Чтобы указать, что должно быть сделано в фоновом режиме, используется очередь. Как плохой побочный эффект, когда рабочий поток что-то делает, пользователь не может делать что-то еще в фоновом режиме, так как есть только один рабочий поток.

Структура событий LabVIEWs сильно отличается от других языков программирования: у событий LabVIEW может быть несколько потребителей! Если используется несколько структур событий, все продолжает работать хорошо (за исключением событий с логическими возвращаемыми значениями). В Windows события публикуются в определенном потоке, в основном в потоке Windows. Для подачи нескольких потоков события должны быть опубликованы несколько раз. Подобно другим языкам программирования. События там обрабатываются чем-то похожим на функции, связанные с «очередью» LabVIEW: если кто-то получает событие, оно выходит из очереди.

Множественное нацеливание требует, чтобы каждый потребитель как-то регистрировался у производителя. Для событий с графическим интерфейсом это делается автоматически. Для пользовательских событий это должно быть сделано программно. См. Примеры LabVIEW.

Распределение событий между несколькими слушателями реализовано в Windows с использованием DDE, но это только для процессов, а не для потоков. Регистрация в потоке выполняется с использованием DdeConnect() или подобного, а события передаются в функцию обратного вызова. (Точнее, как работает Win32, GetMessage() получает сообщения DDE, а DispathcMessage() фактически вызывает функцию обратного вызова.)

0 голосов
/ 11 марта 2010

Если все, что вам нужно, это ввод с клавиатуры, то стандартный ввод-вывод C - это то, что вам нужно. По умолчанию входные потоки буферизируются и блокируют вашу программу до тех пор, пока не будут получены входные данные. Используйте scanf, getchar, все остальное в <stdio.h>.

Если вы хотите вводить мышью, вам нужно быть более конкретным в отношении вашей платформы, так как C / C ++ не имеет встроенной поддержки мыши или окон.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...