C与LabVIEW的事件结构类比是什么?

时间:2010-03-11 02:32:02

标签: c events labview

我在LabVIEW中使用的一个编程结构是Event Structure。这使我不必通过轮询不必要地浪费CPU周期,而只在生成我感兴趣的事件时执行操作。

作为一名经验丰富的LabVIEW程序员,对C有很好的理解,我很好奇如何在C中模拟LabVIEW的事件结构;最好是在Linux下。我们非常感谢一个小代码示例(如上面链接中的示例),说明了如何完成此操作。此外,如果已经存在第三方库(用于Linux)将此事件框架添加到C,那么也很高兴知道。感谢。

5 个答案:

答案 0 :(得分:7)

事件结构实际上只是一个隐藏执行线程的抽象。必须在计算机上某处运行一些代码来检查这些事件,然后调用事件处理程序。在C中,您需要自己提供此代码(程序的“主循环”)。此代码将检查您感兴趣的各种事件源并调用事件处理函数。

然后诀窍就变成了如何不让这个主循环疯狂地旋转CPU。一个简单的技巧是让主循环休眠一段时间,然后检查是否需要处理任何事件,然后再次睡眠。这有引入延迟的缺点。如果适用,更好的技巧是让操作系统在正常操作中执行这些检查,然后在发生有趣事件时唤醒应用程序的主循环。在Linux中,这是通过'select'系统调用完成的,但是select有一个限制,即它只能指定一个可以与文件描述符关联的资源,因此设备,标准输入,文件,网络端口都可以。

编辑:澄清我的downvoters:我不否认硬件中断的存在。是的,如果代码可以直接访问它希望处理的所有事件的硬件中断(例如嵌入式系统或设备驱动程序),您可以编写真正的“事件驱动”代码,其中包含多个不忙等待或睡眠的入口点。但是,在Linux下运行的普通应用程序级C程序中,此代码体系结构实际上并不存在,而是在应用程序级别进行模拟。任何Linux应用程序都将有一个主循环,至少有一个执行线程。该线程可能会被调度程序暂停,但它始终存在并且始终在特定指令处具有指令指针。如果代码离开main(),则程序结束。代码无法从main返回,并且稍后从内核获得回调。代码有一个入口点,必须手动调用其各种事件处理程序。除了设备驱动程序(或使用信号的非常特定的系统代码)之外,如果用户单击某个菜单项而不是代码正在运行,则无法让内核或硬件自动调用某个函数,检测此事件本身,并调用正确的事件处理程序。

你可以告诉LabView“XX发生时调用此功能”。在C中,您可以告诉自己的事件调度代码“XX发生时调用此函数”。

我想说的是什么(糟糕的?)是事件框架体系结构不是C / Linux应用程序的原生体。它必须由您的代码模拟,具有一个主分派线程,它给出了事件驱动框架的外观。您可以手动执行此操作,也可以使用在幕后执行此操作的事件库来提供事件驱动模型的外观。 LabView采用第二种方法,因此在没有事件发生时似乎没有代码在运行,但实际上有LabView自己的C ++代码运行来管理事件队列。这并不意味着它一直在忙着等待,正如我之前所说的那样,系统调用如select和sleep代码可以用来在没有工作时产生cpu时间,但代码不能简单停止执行。

假设你想用两个事件处理程序编写一个“事件驱动”程序。每隔十秒调用一次调用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,你会发现它们的工作原理是这样的。

答案 1 :(得分:2)

我喜欢libev这类事情。

答案 2 :(得分:2)

大多数GUI工具包(GTKQt等)都实现了自己的事件循环抽象。我已经粘贴了一个示例程序here,因为它包含在答案中有点长。它是您使用GTK工具包向C提到的LabVIEW示例的一个端口,因为这是我熟悉的。但是,事件循环的基础知识在其他工具包中并没有太大的不同。

答案 3 :(得分:0)

如果您只关心键盘输入,那么C标准I / O就是您想要的。默认情况下,输入流被缓冲,并将停止程序直到收到输入。使用scanfgetchar<stdio.h>中的其他内容。

如果您想要鼠标输入,则需要更加具体地了解您的平台,因为C / C ++没有对鼠标或窗口的本机支持。

答案 4 :(得分:0)

类似于LabVIEW的事件结构是Win32的“事件拉动”功能GetMessage()GetMessage()会一直等到GUI事件发生。甚至对于Windows中的每个子窗口(LabVIEW:控件或指示器)都有比LabVIEW更多的事件。 GetMessage()只返回每个事件,精细过滤(如在LabVIEW中)必须在以后完成,通常使用DispatchMessage()和Window的事件处理程序WindowProc()及其大小switch() MsgWaitForMultipleObjects() 1}}陈述。

大部分内容都使用“事件推送”风格,这种风格对事件结构来说并不合适。中断驱动的程序。

如果使用超时,请认为PeekMessage()之前调用GetMessage()文件句柄为零DdeConnect()。超时情况适用于在给定时间跨度内没有事件到达的情况。

实际上,LabVIEWs的事件结构应该在一个单独的循环中。一个单独的循环是一个线程。对于典型的Win32编程,主线程中使用GetMessage(),并根据需要通过用户交互生成其他(“worker”)线程。

LabVIEW无法轻松创建线程。它只能通过调用异步SubVI来实现。真!因此,大多数LabVIEW程序使用第二个while循环作为永久可用的工作线程,该线程将在必须完成某些操作时运行并阻塞(即停止消耗CPU功率)。为了指示在后台必须完成的操作,使用了一个队列。 作为一个不好的副作用,当工作线程做某事时,用户不能在后台做其他事情,因为只有一个工作线程。

LabVIEWs事件结构与其他编程语言有很大不同:LabVIEW事件可能有多个消费者!如果使用多个事件结构,一切都会继续正常工作(除了具有布尔返回值的事件)。在Windows中,事件被发布到特定线程,主要是发布到Windows的线程。要提供多个线程,必须多次发布事件。与其他编程语言类似。其中的事件由类似于LabVIEW“队列”相关功能的事件处理:如果有人收到该事件,则它不在队列中。

多目标要求每个消费者以某种方式向生产者注册自己。对于GUI事件,这是自动完成的。对于用户事件,必须以编程方式完成。参见LabVIEW示例。

使用DDE在Windows中实现将事件分发给多个侦听器,但这仅适用于进程而不是线程。使用DispathcMessage()或类似命令注册线程,并将事件推送到回调函数。 (更确切地说,Win32如何工作,[{"adsname":"Francis","adsimageurl":"Andrew Love.jpg","ontop":false,"key":30012647,"onscan":true,"adscode":6689390,"brandname":{"adsbrand":"Beth Moon"},"category":"New ads","adsscription":"Weinstein Jacob Sutton","from":"2016-12-30T00:00:00","to":"2016-12-30T00:00:00"},{"adsname":"McKay","adsimageurl":"Lorraine Spencer.jpg","ontop":false,"key":136301519,"onscan":true,"adscode":346146503,"brandname":{"adsbrand":"Russell Warner"},"category":"New ads","adsscription":"Stanton Thomas Moran","from":"2016-12-30T00:00:00","to":"2016-12-30T00:00:00"},{"adsname":"Berger","adsimageurl":"Lois Norton.jpg","ontop":false,"key":32971839,"onscan":false,"adscode":334075948,"brandname":{"adsbrand":"Becky Park"},"category":"New ads","adsscription":"Gallagher Matthew Pitts","from":"2016-12-30T00:00:00","to":"2016-12-30T00:00:00"},{"adsname":"Boswell","adsimageurl":"Constance Scarborough.jpg","ontop":false,"key":183877654,"onscan":true,"adscode":230154009,"brandname":{"adsbrand":"Yvonne Hardy"},"category":"New ads","adsscription":"Riddle Nancy Atkins","from":"2016-12-30T00:00:00","to":"2016-12-30T00:00:00"}] 接收DDE消息,而public class AdsImportEntity { [JsonProperty(PropertyName = "title")] public string AdsTitle { get; set; } [JsonProperty(PropertyName = "description")] public string Description { get; set; } [JsonProperty(PropertyName = "barcode")] public string Barcode { get; set; } [JsonProperty(PropertyName = "top")] public bool? Top { get; set; } [JsonProperty(PropertyName = "fromdatetime")] public System.DateTime? FromDatetime { get; set; } [JsonProperty(PropertyName = "todatetime")] public DateTime? ToDatetime { get; set; } [JsonProperty(PropertyName = "httpimageurl")] public string HttpImageUrl { get; set; } } 实际上调用了回调函数。)