python网络编程04 异步与同步编程事件驱动
Posted 麦恒
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python网络编程04 异步与同步编程事件驱动相关的知识,希望对你有一定的参考价值。
1、同步模型和异步模型
- 下图展示了,同步单线程、同步多线程以及异步单线程三种模式下程序随着时间的推移所做的工作。这个程序有3个任务需要完成,每个任务都在等待I/O操作时阻塞自身。阻塞在I/O操作上所花费的时间已经用灰色框标示出来了。
1、单线程同步模型
- 在单线程同步模型中,任务按照顺序执行。如果某个任务因为I/O而阻塞,其他所有的任务都必须等待,直到它完成之后它们才能依次执行。这种明确的执行顺序和串行化处理的行为是很容易推断得出的。如果任务之间并没有互相依赖的关系,但仍然需要互相等待的话这就使得程序不必要的降低了运行速度。
2、多线程同步模型
- 在多线程中,这每个任务分别在独立的线程中执行。线程由操作系统管理,并且可以在具有多个处理器或多个内核的系统上真正并发运行,或者可以在单个处理器上交错运行。这使得当某个线程阻塞在某个资源的同时其他线程得以继续执行。与完成类似功能的单线程程序相比,这种方式更有效率,但程序员必须写代码来保护共享资源,防止其被多个线程同时访问。多线程程序更加难以推断,因为这类程序不得不通过线程同步机制如锁、可重入函数、线程局部存储或者其他机制来处理线程安全问题,如果实现不当就会导致出现微妙且令人痛不欲生的bug。
3、异步模型
- 在异步模型程序中,任务交错执行,但仍在一个线程中。当处理I/O或者其他昂贵的操作时,它会转而执行一些仍然可以取得进展的其他任务。
- 单线程异步系统将始终以交错方式执行,即使在多处理器系统上也是如此。注意,可以混合使用异步模型和线程模型,并在同一个系统中使用这两种模型。
- 异步模型的基本思想是,当一个异步程序面临一个通常会在同步程序中阻塞的任务时,它将转而执行一些仍然可以取得进展的其他任务。因此,当没有任务可以取得进展时,异步程序才会“阻塞”,因此称为非阻塞程序。每次从一个任务转换到另一个任务都对应着前一个任务的完成,或者到达一个必须停止的点。有了大量潜在的阻塞任务,异步程序可以通过花费更少的总等待时间,而将大致相同的时间用于单个任务的实际工作,从而比同步程序的性能更好。
4、异步模型和同步模型的区别
- 异步模型比线程模型更简单,因为有一个指令流和任务显式地放弃控制,而不是任意地挂起。
- 异步模型的实现显然比同步模型更复杂。程序员必须把每个任务组织成一个小步骤序列,这些步骤间歇性地执行。如果一个任务使用另一个任务的输出,依赖的任务必须被写入以接受它的输入作为一系列的比特和片段,而不是全部在一起。
- 由于没有实际的并行性,异步程序的执行时间可能和同步程序的执行时间一样长,也许更长因为异步程序可能表现出较差的引用局部性。
5、为什么要选择使用异步模型呢
- 如果一个或多个任务负责为人类实现一个界面,那么通过将这些任务交织在一起,系统可以在“后台”执行其他工作的同时保持对用户输入的响应。因此,虽然后台任务可能不会执行得更快,但系统会让使用者感到更愉快。
- 当任务被迫等待或阻塞时,异步系统的性能会明显优于同步系统,从更短的时间内完成所有任务的意义上来说,尤其优越。
6、与同步模型相比,异步模型在以下情况下性能最好:
- 有大量的任务,所以很可能总有至少一项任务可以取得进展。
- 这些任务执行大量的I/O,导致同步程序浪费大量时间阻塞,而其他任务可能正在运行。
- 这些任务在很大程度上是相互独立的,因此几乎不需要任务间的通信(因此一个任务要等待另一个任务)。
- 这些条件几乎完美地描述了客户机-服务器环境中典型的繁忙网络服务器(如web服务器)。每个任务用I/O表示一个客户端请求,其形式是接收请求并发送应答。客户端请求(主要是读取)在很大程度上是独立的。因此,网络服务器实现是异步模型的主要候选,这就是为什么Twisted首先是一个网络库的原因。
2、事件驱动模型
- 流水式编程:
- 开始--->代码块A--->代码块B--->代码块C--->代码块D--->......--->结束
- 事件驱动模型:
- 开始---->初始化---->等待
- 事件驱动模型可以分为3类。
- 当每收到一个请求的时候,创建一个线程来处理请求。
- 当每收到一个请求的时候,创建一个进程来处理请求。
- 当每收到一个请求的时候,将一个请求放进事件列表,让主进程通过非阻塞I/O方式来处理请求。#(这种方法是指协程事件驱动的方式。)
-
第(1)种方法,由于创建新的进程的开销比较大,所以,会导致服务器性能比较差,但实现比较简单。
第(2)种方式,由于要涉及到线程的同步,有可能会面临死锁等问题。
第(3)种方式,在写应用程序代码时,逻辑比前面两种都复杂。
综合考虑各方面因素,一般普遍认为第(3)种方式是大多数网络服务器采用的方式
1、事件驱动模型
- 目前大部分的UI编程都是事件驱动模型,如很多UI平台都会提供onClick()事件,这个事件就代表鼠标按下事件。事件驱动模型大体思路如下:
- 有一个事件(消息)队列;
- 鼠标按下时,往这个队列中增加一个点击事件(消息);
- 有个循环,不断从队列取出事件,根据不同的事件,调用不同的函数,如onClick()、onKeyDown()等;
- 事件(消息)一般都各自保存各自的处理函数指针,这样,每个消息都有独立的处理函数;
2、事件驱动注解
- 要理解事件驱动和程序,就需要与非事件驱动的程序进行比较。实际上,现代的程序大多是事件驱动的,比如多线程的程序,肯定是事件驱动的。早期则存在许多非事件驱动的程序,这样的程序,在需要等待某个条件触发时,会不断地检查这个条件,直到条件满足,这是很浪费cpu时间的。而事件驱动的程序,则有机会释放cpu从而进入睡眠态(注意是有机会,当然程序也可自行决定不释放cpu),当事件触发时被操作系统唤醒,这样就能更加有效地使用cpu.
- 再说什么是事件驱动的程序。一个典型的事件驱动的程序,就是一个死循环,并以一个线程的形式存在,这个死循环包括两个部分,第一个部分是按照一定的条件接收并选择一个要处理的事件,第二个部分就是事件的处理过程。程序的执行过程就是选择事件和处理事件,而当没有任何事件触发时,程序会因查询事件队列失败而进入睡眠状态,从而释放cpu。
- 事件驱动的程序,必定会直接或者间接拥有一个事件队列,用于存储未能及时处理的事件。
- 事件驱动的程序的行为,完全受外部输入的事件控制,所以,事件驱动的系统中,存在大量这种程序,并以事件作为主要的通信方式。
- 事件驱动的程序,还有一个最大的好处,就是可以按照一定的顺序处理队列中的事件,而这个顺序则是由事件的触发顺序决定的,这一特性往往被用于保证某些过程的原子化。
- 目前windows,linux,nucleus,vxworks都是事件驱动的,只有一些单片机可能是非事件驱动的。
3、事件驱动编程
- 基于事件驱动的程序设计在图形用户界面(GUI)出现很久前就已经被应用于程序设计中,可是只有当图形用户界面广泛流行时,它才逐渐形演变为一种广泛使用的程序设计模式。
- 在过程式的程序设计中,代码本身就给出了程序执行的顺序,尽管执行顺序可能会受到程序输入数据的影响。
- 在事件驱动的程序设计中,程序中的许多部分可能在完全不可预料的时刻被执行。往往这些程序的执行是由用户与正在执行的程序的互动激发所致。
- 事件。就是通知某个特定的事情已经发生(事件发生具有随机性)。
- 事件与轮询。轮询的行为是不断地观察和判断,是一种无休止的行为方式。而事件是静静地等待事情的发生。事实上,在Windows出现之前,采用鼠标输入字符模式的PC应用程序必须进行串行轮询,并以这种方式来查询和响应不同的用户操做。
- 事件处理器。是对事件做出响应时所执行的一段程序代码。事件处理器使得程序能够对于用户的行为做出反映。
- 事件驱动常常用于用户与程序的交互,通过图形用户接口(鼠标、键盘、触摸板)进行交互式的互动。当然,也可以用于异常的处理和响应用户自定义的事件等等。
- 事件的异常处理比用户交互更复杂。
- 事件驱动不仅仅局限在GUI编程应用。但是实现事件驱动我们还需要考虑更多的实际问题,如:事件定义、事件触发、事件转化、事件合并、事件排队、事件分派、事件处理、事 件连带等等。
- 到目前为止,我们还没有找到有关纯事件驱动编程的语言和类似的开发环境。所有关于事件驱动的资料都是基于GUI事件的。
- 属于事件驱动的编程语言有:VB、C#、Java(Java Swing的GUI)等。它们所涉及的事件绝大多数都是GUI事件。
# #
以上是关于python网络编程04 异步与同步编程事件驱动的主要内容,如果未能解决你的问题,请参考以下文章
nodejs所用的概念(同步,异步,事件驱动,事件循环等)通俗解释
# 进程/线程/协程 # IO:同步/异步/阻塞/非阻塞 # greenlet gevent # 事件驱动与异步IO # SelectPollEpoll异步IO 以及selectors模块 # (示