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请求htmlurl = urlparse(url)host = url.netlocpath = url.pathif path == "":path = "/"# 建立socket连接client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 这里会导致后边抛异常,但是连接请求已经发出去了client.setblocking(False)# 捕获异常try:client.connect((host, 80)) # 阻塞不会消耗cpuexcept 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"))breakexcept OSError as e:passdata = b""while True:# 这里还会抛异常,读不到就继续读try:d = client.recv(1024)except BlockingIOError as e:continueif d:data += delse:breakdata = 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_WRITEselector = DefaultSelector()
urls = []
stop = Falseclass 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 += delse:# 数据读完为空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 stopstop = Truedef get_url(self, url):self.spider_url = urlurl = urlparse(url)self.host = url.netlocself.path = url.pathself.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)) # 阻塞不会消耗cpuexcept BlockingIOError as e:passselector.register(self.client.fileno(), EVENT_WRITE, self.connected)# 驱动整个事件循环
def loop():while not stop:ready = selector.select()for key, mask in ready:call_back = key.datacall_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函数负责驱动整个事件循环。
回调的缺点
- 可读性差
- 共享状态异常处理
- 异常处理困难
Python高级知识点学习(九)相关推荐
- Python高级知识点学习(一)
image.png Python中一切皆对象 和Java相比,Python的面向对象更加彻底. 函数和类也是对象,是python的一等公民. 代码和模块也可以称之为对象. python中的类也是对象, ...
- 郑州学python_郑州Python基础知识点学习之内置类型
想要学好Python,一定要学好各类知识点,比如类.对象.数据类型等.有部分同学对于内置类型概念模糊,接下来千锋小编分享的郑州Python基础知识点汇总就给大家简单梳理一下. 内置类型是指任何语言在设 ...
- Python高级知识点汇总第一部
Linux常见命令 ls:查看当前路径下的所有文件及文件名; clear:清空操作; cd 跳转操作,可以跳转到任意路径位置; cd-:跳转到上次所在的路径; cd~:快速切换到当前用户的主目录(快速 ...
- python知识点总结全_【转】Python高级知识点总结
一.可迭代对象.迭代器对象和生成器 像list, tuple等这些序列是可以使用for...in ...语句来进行遍历输出的.这是为什么呢?这就需要知道可迭代对象(Iterable).迭代器对象(It ...
- python网络平台_python学习(九) 网络编程学习--简易网站服务器
python `网络编程`和其他语言都是一样的,服务器这块步骤为: `1. 创建套接字` `2. 绑定地址` `3. 监听该描述符的所有请求` `4. 有新的请求到了调用accept处理请求` Pyt ...
- c++面向对象高级编程 学习九 pointer-like classes
c++的class设计出来有两种形式,一种像指针,一种像函数 智能指针里包含普通指针,要写 * 和 -> 的函数 sp->method(); //sp-> 经 T* operator ...
- 怎样学好python编程-怎样学习python编程?
什么是Python? 在过去的2017年里,Python开发者在全球快速增长,国内小伙伴学习 Python 的热情一路高涨.同时,PYPL发布7月编程语言指数榜,Python 在今年5月首次超越 Ja ...
- Python学习笔记(五) Python高级特性
Python高级特性 一. 切片 python中提供了切片(Slice)操作符 , 可以方便的获取list或tuple中的某一段元素 . # -*- coding : utf-8 -*- #Pytho ...
- 强烈推荐这11个Python开源项目,非常值得入门学习(从入门到Python高级开发)
Python的日益普及及其在业界的使用使它成为当今最流行的编程语言之一.即使有很多学习Python的资源,例如参考书,视频教程,网站,您也可以将GitHub视为满足学习Python愿望的可靠资源之一. ...
最新文章
- 用无人车硬件玩GTA 5,这个12岁孩子的外挂有点硬核
- javascript中的表结构
- mstem函数怎么定义_PYTHON--函数定义
- android - 常用知识点以及代码片段(不断更新)
- 本科毕设论文——基于Kinect的拖拉机防撞系统
- 腾讯信息流推荐业务实践:内容分发场景的多目标架构实践
- Java 连接 SQL Server 数据库
- 描绘质量属性的六个常见属性场景。
- 别催更啦!手淘全链路性能优化下篇--容器极速之路
- 手工做迷宫_手工DIY好玩双人大型立体迷宫玩具
- 最牛逼的PHP视频教程115网盘免费下载,嗷嗷给力
- 所有安卓手机通刷原生系统
- iOS xcode中生成和打包ipa文件的方法和步骤
- 一种简单的仓储系统实物可视化分布实现方案
- 树莓派 11 bullseye镜像官方源和国内源
- sqlserver2012用ip远程连接设置
- 深入浅出低功耗蓝牙(BLE)协议栈,使用Ubertooth one扫描嗅探低功耗蓝牙
- Linux学习总结(59)——为什么建议大家使用 Linux 开发
- Mac 修改AppleID 使用“登录”钥匙串
- web免登钉钉微应用
热门文章
- 如何快速的入门Docker并且实现部署
- git 子模块_Git子模块的问题
- 桩位编号插件xzbh_饶平专业泵站基坑拉森钢板桩施工规范
- brew安装php-ffmpeg,mac 系统编译安装ffmpeg
- 哈斯机床进去debug模式_责任链模式
- arraylist扩容是创建新数组吗 java_Java集合干货——ArrayList源码分析
- Tomcat 5 5 JNDI Resource 配置
- 快速编写json数据
- 错误Cannot resolve org.springframework.data:spring-data-redis:2.2.6 RELEASE
- Java学习笔记2.2.1 常量与变量 - 变量