Python高级知识点学习(九)
并发、并行,同步、异步,阻塞、非阻塞
并发、并行
- 并发是在一个时间段内,有几个程序在同一个cpu上运行,但是任意时刻只有一个程序在cpu上运行。
- 并行是任意时刻点上,有多个程序同时运行在多个cpu上。
同步、异步
- 同步是指代码调用IO操作时,必须等待IO操作完成才返回的调用方式。
- 异步是指代码调用IO操作时,不必等待IO操作完成就返回的调用方式。
阻塞、非阻塞
- 阻塞是指调用函数时候当前线程被挂起。
- 非阻塞是指调用函数时候当前线程不会被挂起,而是立即返回。
阻塞和非阻塞的概念和同步异步感觉很像,但是其实它们之间是有区别的。
区别:
同步和异步实际上是消息通信的一种机制,可以把IO操作看做一个消息,调用IO操作时,相当于发一个消息给另外一个线程(或者说另外一个协程),让它去执行某些操作,在提交数据之后立刻得到future,后边就可以通过future拿到结果,实际上是消息之间的通信机制。
阻塞和非阻塞是不同于同步异步的,它是函数调的一种机制。
IO 多路复用 (select、poll 和 epoll)
unix中五种I/O模型
- 阻塞式I/O
- 非阻塞式I/O
- I/O复用
- 信号驱动式I/O (用的比较少)
- 异步I/O (POSIX的aio_系列函数)
以上五种是递进式的发展。
I/O多路复用:
select方法也是阻塞的方法,select本身是阻塞式的,select可以监听多个文件句柄和socket,select在某一个文件句柄或者socket准备好的话就会返回,这时候立刻可以做业务逻辑处理。
I/O多路复用带来的好处是:
比如现在同时发起了100个非阻塞式的请求,这时候直接使用select去监听这100个socket,这样的话一旦有一个发生状态变化,我们就可以立马处理它。
I/O多路复用中,将数据从内核复制到用户空间这段时间消耗还是省不了。
异步IO:
这里的异步IO是真正意义上的异步IO(aio),我们现在接触到很多高并发框架实际上都没有使用异步IO,实际上在很大程度上使用的都是io多路复用技术,IO多路复用很成熟很稳定,异步IO对于IO多路复用性能提升并没有达到很明显的程度,但是编码难度有很大提升,所以当前情况下IO多路复用用的比较多。
异步IO节省掉了数据从内核拷贝到用户空间这一步骤。
select、poll、epoll:
select、poll、epoll都是I/O多路复用的机制。
I/O多路复用就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般就是读就绪或写就绪),能够通知程序进行相应的读写操作。
但select、poll、epoll本质上都是同步I/O,因为它们都需要在读写事件就绪后自己负责进行读写(数据从内核拷贝到用户区),也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。
select是什么?
select 函数监视的文件描述符分三类,分别是writefds、readfds、exceptfds。调用select后会阻塞,直到有描述符就绪(有数据可读、可写、或者有except),或者超时(timeout指定等待时间,如果立即返回设为null即可),函数返回。当select函数返回后,可以通过遍历fdset来找到就绪的描述符。
select目前几乎在所有的平台上支持,其良好的跨平台也是它的一个优点。select的一个缺点在于单个进程监视的文件描述符的数量有最大限制,在linux上一般为1024,可以通过修改宏定义甚至重新编译内核的方式提升这一性质,但是这样也会造成效率的降低。
poll是什么?
不同于select使用三个位图来表示三个fdset的方式,poll使用一个pollfd的指针实现。pollfd结构包含了要监视的event和发生的event,不再使用select "参数-值" 传递的方式。同时,pollfd并没有最大数量限制(但是数量过大性能也会下降)。和select函数一样,poll返回后,需要轮询pollfd来获取就绪的描述符。
从上面看,select和poll都需要在返回后,通过遍历文件描述符来获取已经就绪的socket。事实上,同时连接的大量客户端在一时刻可能只有很少的处于就绪的状态,因此随着监视的描述符数量的增长,其效率也会线性下降。
epoll是什么?
epoll是在2.6内核中提出的,epoll是之前的select和poll的增强版本。相对于select和poll来讲,epoll更加灵活,没有描述符限制。epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需要一次。
epoll它的查询使用了数据结构中性能很高的一个:红黑树。
nginx就是使用了epoll。
epoll并不代表一定比select好:
- 在并发高的情况下,连接活跃度不是很高, epoll比select。
- 并发性不高,同时连接很活跃, select比epoll好。
非阻塞I/O实现http请求
上示例代码:
import socket from urllib.parse import urlparse def get_url(url): # 通过socket请求html url = urlparse(url) host = url.netloc path = url.path if path == "": path = "/" # 建立socket连接 client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 这里会导致后边抛异常,但是连接请求已经发出去了 client.setblocking(False) # 捕获异常 try: client.connect((host, 80)) # 阻塞不会消耗cpu except BlockingIOError as e: pass # 不停的询问连接是否建立好, 需要while循环不停的去检查状态 # 做计算任务或者再次发起其他的连接请求 while True: try: client.send("GET {} HTTP/1.1\r\nHost:{}\r\nConnection:close\r\n\r\n".format(path, host).encode("utf8")) break except OSError as e: pass data = b"" while True: # 这里还会抛异常,读不到就继续读 try: d = client.recv(1024) except BlockingIOError as e: continue if d: data += d else: break data = data.decode("utf8") html_data = data.split("\r\n\r\n")[1] #打印返回的数据 print(html_data) client.close() if __name__ == "__main__": get_url("http://www.baidu.com")
非阻塞I/O整个过程依赖前后的监测,整个过程不停的做while循环检测状态,但是返回时间没有变,所以并没有提高并发。
select+回调+事件循环实现http请求
目前开源的高性能框架,一般都是使用这种方式实现并发。
使用select + 回调 + 事件循环实现下载网页,并发性高且是单线程。
select方法本尊是在import select
这个包里边,但是有另外一个包把select基础上进行了封装,用起来更简单:from selectors import DefaultSelector
,DefaultSelector一般使用DefaultSelector这个比较多。
看代码示例:
import socket import time from urllib.parse import urlparse from selectors import DefaultSelector, EVENT_READ, EVENT_WRITE selector = DefaultSelector() urls = [] stop = False class Fetcher: def connected(self, key): selector.unregister(key.fd) self.client.send("GET {} HTTP/1.1\r\nHost:{}\r\nConnection:close\r\n\r\n".format(self.path, self.host).encode("utf8") selector.register(self.client.fileno(), EVENT_READ, self.readable) # 当socket可读时,读数据,全部都是cpu操作 def readable(self, key): d = self.client.recv(1024) if d: self.data += d else: # 数据读完为空 selector.unregister(key.fd) data = self.data.decode("utf8") html_data = data.split("\r\n\r\n")[1] print(html_data[:30]) self.client.close() urls.remove(self.spider_url) if not urls: global stop stop = True def get_url(self, url): self.spider_url = url url = urlparse(url) self.host = url.netloc self.path = url.path self.data = b"" if self.path == "": self.path = "/" # 建立 socket 连接 self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.client.setblocking(False) try: self.client.connect((self.host, 80)) # 阻塞不会消耗cpu except BlockingIOError as e: pass selector.register(self.client.fileno(), EVENT_WRITE, self.connected) # 驱动整个事件循环 def loop(): while not stop: ready = selector.select() for key, mask in ready: call_back = key.data call_back(key) if __name__ == "__main__": # 计时开始 start_time = time.time() for url in range(60): url = "http://www.baidu.com" urls.append(url) fetcher = Fetcher() fetcher.get_url(url) loop() print(time.time()-start_time)
上边代码中,Fetcher类包含三个方法,get_url简历socket连接,connected和readable是两个回调函数。
loop函数负责驱动整个事件循环。
回调的缺点
- 可读性差
- 共享状态异常处理
- 异常处理困难

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
layui 栅格系统与后台布局
栅格系统 为了丰富网页布局简化HTML/CSS代码的耦合,并提升多终端的适配能力,layui在2.0版本中引进了自己的一套具备响应式能力的栅格系统。我们将容器进行了12等分,预设了4*12种CSS排列类,它们在移动设备、平板、电脑中/>大尺寸四种不同的屏幕下发挥着各自的作用。 一、栅格布局规则: 采用 layui-row 来定义行,如:<div class="layui-row"></div> 采用类似 layui-col-md* 这样的预设类来定义一组列(column),且放在行(row)内。其中: 变量md 代表的是不同屏幕下的标记(可选值见下文) 变量* 代表的是该列所占用的12等分数(如6/12),可选值为 1 - 12 如果多个列的“等分数值”总和等于12,则刚好满行排列。如果大于12,多余的列将自动另起一行。 列可以同时出现最多四种不同的组合,分别是:xs(超小屏幕,如手机)、sm(小屏幕,如平板)、md(桌面中等屏幕)、lg(桌面大型屏幕),以呈现更加动态灵活的布局。 可对列追加类似 layui-col-space5、 layui-col-m...
- 下一篇
基于OpenSSL的HTTPS通信C++实现
HTTPS是以安全为目标的HTTP通道,简单讲是HTTP的安全版。即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL。Nebula是一个为开发者提供一个快速开发高并发网络服务程序或搭建高并发分布式服务集群的高性能事件驱动网络框架。Nebula作为通用网络框架提供HTTPS支持十分重要,Nebula既可用作https服务器,又可用作https客户端。本文将结合Nebula框架的https实现详细讲述基于openssl的SSL编程。如果觉得本文对你有用,帮忙到Nebula的Github或码云给个star,谢谢。Nebula不仅是一个框架,还提供了一系列基于这个框架的应用,目标是打造一个高性能分布式服务集群解决方案。Nebula的主要应用领域:即时通讯(成功应用于一款IM)、消息推送平台、数据实时分析计算(成功案例)等,Bwar还计划基于Nebula开发爬虫应用。 1. SSL加密通信 HTTPS通信是在TCP通信层与HTTP应用层之间增加了SSL层,如果应用层不是HTTP协议也是可以使用SSL加密通信的,比如WebSocket协议WS的加上SSL层之...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- 设置Eclipse缩进为4个空格,增强代码规范
- SpringBoot2整合Redis,开启缓存,提高访问速度
- Windows10,CentOS7,CentOS8安装Nodejs环境
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果