Какова аналогия C со структурой событий LabVIEW?


person SiegeX    schedule 11.03.2010    source источник
comment
Базовый язык не предоставляет такие возможности. Вы используете библиотеки или API ОС.   -  person dmckee --- ex-moderator kitten    schedule 11.03.2010
comment
Возможно, вы захотите посмотреть, как это делает CVI от National Instruments. Я не уверен, доступен ли CVI для Linux. zone.ni.com/reference/en-XX /help/370051K-01/cvi/   -  person Anzurio    schedule 11.03.2010


Ответы (5)


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

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

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

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

Я пытаюсь сказать (плохо?), что архитектура платформы событий не является родной для приложения C/Linux. Он должен эмулироваться вашим кодом с помощью основного потока диспетчеризации, который создает видимость среды, управляемой событиями. Либо вы делаете это вручную, либо используете библиотеку событий, которая делает это за кулисами, чтобы создать видимость модели, управляемой событиями. LabView использует второй подход, так что кажется, что никакой код не выполняется, когда не происходит никаких событий, но на самом деле существует собственный код LabView C++, управляющий очередями событий. Это не означает, что он все время занят ожиданием, как я уже говорил, есть системные вызовы, такие как 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()

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

person bdk    schedule 11.03.2010
comment
В опросе нет необходимости, если аппаратное обеспечение обеспечивает адекватный механизм прерывания и его поддерживает ОС. Никто не любит напряженного ожидания. - person dmckee --- ex-moderator kitten; 11.03.2010
comment
Определенно правда. Я подумал о том, чтобы добавить пункт об особом случае для обработчиков прерываний, но решил, что это не очень полезно для проблемы, которую пытается решить вопрос. Если ваш код работает на уровне приложения, а не в ядре, вы не не имеют прямого доступа к обратным вызовам прерывания. Вместо этого вы получаете уведомления от обработчика прерываний косвенно через системный вызов select, упомянутый выше. - person bdk; 11.03.2010
comment
К сожалению, большинство аппаратных средств основано на прерываниях. Устройства, которые опрашиваются, опрашиваются в ответ на периодическое прерывание таймера в любой удаленной современной ОС. - person Potatoswatter; 11.03.2010
comment
@bdk Я немного сбит с толку, потому что на нескольких учебных занятиях LabVIEW они специально противопоставляют структуру событий опросу. Чтобы добавить к этому, я могу физически увидеть разницу в использовании ЦП между использованием структуры событий и опросом с оператором ожидания. Мне трудно поверить, что опрос происходит за кулисами, потому что для того, чтобы использование ЦП с помощью опроса было настолько низким, насколько я получаю с помощью событий, мое время отклика зашкаливает, поскольку мне приходится так долго ждать/спать. - person SiegeX; 11.03.2010
comment
Это связано с тем, что опрос на любом языке, на котором реализован LabVIEW (если не ошибаюсь, на C++), намного эффективнее, чем опрос в самом LabVIEW. - person ptomato; 11.03.2010
comment
Базовая реализация, которую использует LabVIEW, может использовать конструкции для сна или ожидания аппаратных прерываний для определенных событий, связанных с устройством, таких как ожидание последовательного ввода, и в этом случае приложение, скорее всего, ожидает вызова select и не использует ЦП. Однако для событий высокого уровня, таких как Clicked on File Menu, код не может просто заснуть и ждать, пока какое-либо аппаратное прерывание не поступит для этого события. Вместо этого код внутри приложения должен отслеживать низкоуровневые события мыши и генерировать высокоуровневые события. В LabView это делается под капотом, а не в вашем коде. - person bdk; 11.03.2010
comment
В любом случае, первое предложение «Структура события» на самом деле является просто абстракцией, которая скрывает от вас опрос. неверно и вводит в заблуждение и должно быть удалено. Он абстрагируется от select() или чего-то подобного, а не от опроса. - person reinierpost; 11.03.2010
comment
@reinerpost: Спасибо, я уточнил ответ. Я думаю, вы правы, что вводите в заблуждение. В Linux системные вызовы почти такие же. Посмотрите справочную страницу для системного вызова poll() на машине Unix. По функциональности он почти идентичен select(). На самом деле один вызов часто является просто оболочкой для другого. В этом контексте говорить, что что-то абстрагируется от poll() или select(), на самом деле мудрить. Однако за пределами этой терминологии термин «опрос» обычно используется как синоним «занятого ожидания», поэтому я думаю, что именно здесь я сбился с пути. - person bdk; 11.03.2010
comment
@bdk: Спасибо за обновленный ответ. Я обязательно рассмотрю вызов select(), чтобы увидеть, как это работает. Тем временем я прочитал о сигналах и собрал этот код вместе codepad.org/voFxsStr, который, кажется, приближается Структура событий довольно хорошо. Однако у меня есть ощущение, что в своем стремлении сделать родительское событие управляемым я просто перенес неизбежную обработку, о которой вы говорите, на дочерний элемент, это правильно? Слово предостережения, код отлично работает на первой итерации, но после этого становится дурацким, если вы не нажмете «q» на второй. Интересно почему, но не по теме. - person SiegeX; 11.03.2010

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

person Jack    schedule 11.03.2010

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

person ptomato    schedule 11.03.2010

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

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

person Potatoswatter    schedule 11.03.2010

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

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

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

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

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

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

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

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

person user2699548    schedule 10.11.2016