事件仅用于GUI编程吗?


57

事件仅用于GUI编程吗?

当另一件事发生某些事情时,您如何处理普通的后端编程?


6
顺便说一句,事件源是事件编程的一个完全正交的概念。事件源的基本概念是将“事件”或“变更”存储到系统中,而不是存储系统的“状态”。例如,您可以将银行帐户建模为a)余额(STATE)或b)一系列交易(EVENTSOURCE)。
ArT

4
根据用法,“事件”通常只是某种形式的用糖包裹的回调。回调无处不在-如果您感兴趣的话,这可能是开始搜索的好关键字。
J ...

10
我还将指出,即使您像微控制器一样低级,您也会发现硬件中断是一项有用且值得商essential的基本功能。非常适合用于控制系统或像咖啡机这样的基本IO。这些硬件中断实际上与事件没有本质上的区别。
丹·丹(Dan

不!实际示例:Node.js事件
sampathsris '16

2
您在Windows上吗?检出事件查看器。玩得开心。
Marc.2377

Answers:


106

不。它们对于实现Observers并确保类禁止修改很方便。

假设我们有一个注册新用户的方法。

public void Register(user) {
    db.Save(user);
}

然后有人决定应该发送电子邮件。我们可以这样做:

public void Register(user) {
    db.Save(user);
    emailClient.Send(new RegistrationEmail(user));
}

但是我们刚刚修改了一个本应禁止修改的类。对于这种简单的伪代码来说可能不错,但是可能会使生产代码疯狂。这种方法需要30行代码才与创建新用户的最初目的几乎没有关系?

让类执行其核心功能并引发一个事件来告知正在侦听的用户,该用户会更好,他们可以采取所需的任何操作(例如发送电子邮件)。

public void Register(user) {
    db.Save(user);

    RaiseUserRegisteredEvent(user);
}

这样可以使我们的代码干净灵活。OOP中经常被忽略的部分之一是类之间相互发送消息事件是这些消息。


37
我读了这篇文章,想到了我们的“创建预订”代码,我哭了一会儿,渴望一个更好的地方:'(
sara

1
+1,好答案。只是一个切线问题(这让我有些烦恼):您是否有特殊的原因以大写字母开头您的方法名称(注册,保存和发送)?当然,这并不影响此答案的有用性。
Pedro A

14
@Hamsteriffic我主要是C#开发人员,这是公认的惯例。没有其他原因。
RubberDuck

6
@Hamsterifficas另外,您称为lowerCase但在中间包含一个大写字母的名称通常称为camelCase,因为它在中间有驼峰。这就与snake_case区别开来,snake_case中的小写单词由下划线分隔,Python和大多数shell语言都很熟悉,仅举几例。
亚伦

5
我会说,事件是这些消息的一种。方法调用也应视为消息传递。
jpmc26 2016年

53

不。

非GUI逻辑中使用的事件的经典示例是数据库触发器。

触发器是在发生给定事件(INSERT,DELETE等)时执行的代码。对我来说似乎是一件大事。

这是事件的维基百科定义:

在计算中,事件是软件可以识别的动作或事件,可以由软件处理。计算机事件可以由系统,用户或其他方式生成或触发。通常,事件是与程序流程同步处理的,也就是说,软件可能具有一个或多个处理事件的专用位置,通常是事件循环。事件源包括用户,该用户可以通过例如键盘上的击键与软件进行交互。另一个来源是硬件设备,例如计时器。软件还可以将自己的事件集触发到事件循环中,例如,传达任务的完成情况。响应事件而改变其行为的软件通常被认为是事件驱动的,目的是实现交互。

并非所有事件都是用户生成的。有些是由计时器生成的,例如crontab的计时器,是由数据库INSERT生成的,就像我之前提到的那样。

该定义还指出,某些程序或系统是“事件驱动的,通常以交互性为目标”,从中可以推论出事件的目的或用途不仅是为了提供交互性,而且还常常是为了提供交互性(例如GUI) (但不一定是GUI,因为CLI程序也可以是交互式的)。


2
当我听说数据库触发器时,我总是会想到这一点:thecodelesscode.com/case/42
Almo

作为一名前DBA开发人员,每次听到人们谈论使用触发器而没有考虑更广泛的DB性能时,我都会感到畏缩。
三值逻辑

28

实际上,基于事件的编程也用于高性能的服务器编程。

在典型的服务器工作负载下,处理结果的大部分时间实际上来自I / O。例如,从(7200 RPM)硬盘驱动器中提取数据最多可能需要8.3 ms。对于现代GHz处理器,这相当于约100万个时钟周期。如果CPU每次都在等待数据(不执行任何操作),我们将失去很多时钟周期。

传统的编程技术通过引入多个线程来解决此问题。CPU尝试同时运行数百个线程。但是,该模型的问题在于,每次CPU切换线程时,都需要数百个时钟周期才能进行上下文切换。上下文切换是指CPU将线程本地内存复制到CPU的寄存器中,并将旧线程的寄存器/状态存储到RAM中。

另外,每个线程必须用完一定数量的内存来存储其状态。

如今,人们一直在推动具有单线程且循环运行的服务器。然后,将工作推送到消息泵上,该消息泵充当单个线程的队列(非常类似于UI线程)。CPU无需等待工作完成,而是为硬盘驱动器访问之类的事件设置回调事件。这减少了上下文切换。

这种服务器的最佳示例是Node.js,它已被证明能够使用适度的硬件处理100万个并发连接,而​​Java / Tomcat服务器则要花费数千美元。


2
“适度的硬件”有点误导。除了操作系统使用的内容外,您还需要8GB以上的Node。而且,如果您有那么多内存,Tomcat可以轻松处理数千个连接。当然,有很大的不同,但不是1000倍。
Paul Draper

@PaulDraper不,它不能。不,它没有。您仅需要8GB +即可容纳8000个线程的堆栈。那是最大的不同。
ArT

3
按照服务器标准,除了8GB以外,@ PaulDraper还是非常适中的。我曾在内存为128GB的机器上工作,而这些机器甚至还没有完全加载。摇杆的成本比整台机器高。
ArT

这取决于您的堆栈大小。在大多数平台上,对于64位,Oracle / OpenJDK默认为1MB,对于32位,默认为512K。如果只采用默认值,那将是正确的。但是默认值不是如何获得1M Node连接的方式;)无论如何,128K足够了;您可以获得更少的收益。那将是8000个线程的1GB堆栈空间。
Paul Draper

6
你误会了 即使使用默认的堆栈大小,您也只需要8 GiB的虚拟内存。内存根据需要即时提交。通常,每个堆栈只需要一个页面,除非您实际上在使用额外的内存。实际上,在这样的系统中,大多数线程仅具有那些(通常)64-kiB堆栈。在32位操作系统上,虚拟内存使用量很大,但在64位操作系统上,虚拟内存使用量却不那么重要。您很快就会遇到其他限制-例如TCP端口耗尽:)您认为节点的那些“伪堆栈”存储在哪里?是的,在堆上。
罗安

10

事件在网络编程中(例如Nginx)也被大量使用,以避免昂贵的繁忙等待循环,而是提供一个干净的接口来确切地知道何时可以进行某些操作(I / O,紧急数据等)。这也是解决C10k问题的方法

基本思想是为操作系统提供一组套接字(即网络连接)来监视事件,这些套接字是所有事件还是您特别感兴趣的事件(例如,可读取的数据);当操作系统在列表中的一个套接字上检测到此类活动时,您将收到API所寻找事件的通知,然后您必须对该事件的来源进行分类并采取相应措施。

现在,这是一个低层次的抽象视图,而且很难很好地进行扩展。但是,有很多更高层次的框架甚至可以跨平台地处理这些问题:我想到了Twisted for Python,Boost.Asio for C ++或libevent forC。


+1“昂贵的繁忙等待循环”:换句话说,它在涉及一些不活动(等待)以及此类过程之间的同步(消息或事件)的任何并行过程中很有用。就像现实世界中的许多作品一样。
fr13d

有趣的是,在网络存在之前,套接字最初是作为单台IPC的一种形式设计的。

5

嵌入式系统几乎总是固有地由事件驱动,即使它们没有明确地进行编程也是如此。

这些事件来自硬件中断,按钮按下,周期数模读数,计时器到期等事件。

低功耗嵌入式系统更有可能是事件驱动的。他们将大部分时间都花在睡眠上(CPU在低功耗模式下睡眠),等待某些事情发生(“某事”是一个事件)。

量子平台(QP)是事件驱动的嵌入式系统最常用和最受欢迎的框架之一(该QP还可在Linux,Windows和任何类似Unix的OS下运行。)状态机自然适合事件驱动的编程,因为程序在一般意义上不是“顺序”的,而是根据系统状态和当前事件调用的一组“回调”。


3

事件消息 Gregor Hohpe。

事件驱动架构 Gregor Hohpe。

SEDA体系结构,威尔士,库勒,布鲁尔。

当发生某些事情时,如何处理普通的后端编程呢?

有限状态机是一种常见的方法

Given(State.A)
When(Event.B)
Then(State.C)
    .and(Consequences.D)

2
1,这实际上不是一个连贯的答案; 2,FSM并不是事件使用的好例子。
whatsisname 2016年

1
FSM。我敢肯定,面食宗教会观察到许多事件;-)
fr13d

0

在嵌入式系统中,事件发生在中断期间。从计时器到I / O,有许多中断源。

此外,RTOS也可以具有事件。一个示例是等待来自另一任务的消息。


0

对于非嵌入式系统,但是我在C#中所做的是SCADA系统。当卸载负载时,有许多事件与仓库中发生的事情相关,这是系统生成的事件的一部分,而另一部分是将新状态写入数据库。我们当然有一些GUI客户端,但这只是为了显示反映仓库状态的数据库状态。因此,它是基于事件和线程的后端服务器软件。发展颇具挑战性。

https://zh.wikipedia.org/wiki/SCADA

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.