通过一个小案例深入理解IO多路复用

假如我们现在有这样一个普通的需求,写一个简单的爬虫来爬取校花网的主页

import requests
import timestart = time.time()url = 'http://www.xiaohuar.com/'
result = requests.get(url).textprint(result)
print(time.time()-start)

  

  这样子是显然没啥问题的,总共耗时约为6秒

但是有没有办法更进一步优化呢,这里如果需要优化我们首先需要知道一个知识点

就是requests这个模块它底层其实是封装了urllib2和urllib3的,而这两个模块底层其实就是socket

如果需要优化,从requests是实现不了的,那么能不能从socket来呢

如果从socket,又该如何优化呢?

首先我们得知道socket到底做了什么,

import socketclient = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
url = 'www.xiaohuar.com/'
client.connect((url, 80))
client.send("GET {} HTTP/1.1\r\nHost:{}\r\nConnection:close\r\n\r\n".format('/',80).encode('utf8'))
data = b''
while 1:d = client.recv(1024)if d:data +=delse:breakprint(data)

  这里的代码就是上面那个requests版本的代码的底层

  在这一坨代码中,有几个点需要注意

  connect和recv,这两个方法都是阻塞io,也就是说,如果连接不到或者接受不到消息的话,程序就会一直等,等到预期的效果为止。

  这就是阻塞

  阻塞有个很大的弊端,那就是cpu无法得到充分利用,因为等待的时间里,cpu是空闲的,而我们又没有执行其他的操作,那么这段时间我们能不能充分利用起来呢

  答案是肯定的,socket提供了一个非阻塞的办法

  

client.setblocking(False)

  直接运行试试效果

  BlockingIOError: [WinError 10035] 无法立即完成一个非阻止性套接字操作。

  结果是抛出了这个异常,这是因为当变为非阻塞时候,连接校花网的url的时候,三次握手还没建立完成,我们就去执行下一步了

  

try:client.connect((url, 80))
except BlockingIOError as e:  #处理其他事情pass

  那么我们可以这样改,抓到这个异常但是不处理,这样子,我们就能在except后面加入其他的代码了,也就是说cpu发个请求就不管了,然后去执行后面的代码,这样效率就提高了。

  再运行一次。

  OSError: [WinError 10057] 由于套接字没有连接并且(当使用一个 sendto 调用发送数据报套接字时)没有提供地址,发送或接收数据的请求没有被接受。

  又抛出了一个异常,和上面的原理差不多,因为是非阻塞模式

最终代码如下

import socketclient = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.setblocking(False)
url = 'www.xiaohuar.com'
try:client.connect((url, 80))
except BlockingIOError as e:passwhile 1:try:client.send("GET {} HTTP/1.1\r\nHost:{}\r\nConnection:close\r\n\r\n".format('/',80).encode('utf8'))breakexcept Exception as e:passdata = b''
while 1:try:d = client.recv(1024)except Exception as e:continueif d:data += delse:breakprint(data)

  这样子虽然有一段时间更充分利用了cpu 但是代码很乱,很麻烦,其次虽然是非阻塞,但是有两个地方只是把之前的阻塞的时间花费了在循环上,那么有没有更好的办法呢?

这里就要引入IO多路复用的概念了

IO复用就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(读或者写),都能够通知程序来进行相应的读写操作,但是select,poll和epoll都是同步io,也就是说这个读写过程是阻塞的,而异步io则无需自己进行读写,异步io的实现会负责把数据从内核拷贝到用户内存。

select在windows,OS X, 或者linux都能用,但是select最大监视数量只能为1024

而poll的话其他几乎与select一样,只是突破了最大限制

而epoll就与前面这两个都不一样了,它底层使用了红黑树的数据结构,epoll使用一个文件描述符来管理多个文件描述符,将用户关系的文件描述符的事件存放到内核的一个事件表之中,这样在用户空间和内核空间的copy只需一次。

而poll和select都是才用轮询的方式,所以效率差就在这里体现出来了

最终代码 异步IO

from selectors import DefaultSelector, EVENT_READ, EVENT_WRITE
import socketselector = DefaultSelector()class Fetcher():def send_msg(self, key):selector.unregister(key.fd)self.client.send("GET {} HTTP/1.1\r\nHost:{}\r\nConnection:close\r\n\r\n".format('/', 80).encode('utf8'))selector.register(self.client.fileno(), EVENT_READ, self.recv)def recv(self, key):d = self.client.recv(1024)if d:self.data += delse:selector.unregister(key.fd)print(self.data.decode('utf8'))def get_url(self, url):self.data = b''try:self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)self.client.connect((url, 80))except Exception as e:# 加入另外的逻辑passselector.register(self.client.fileno(), EVENT_WRITE, self.send_msg)def loop_forever():while 1:ready = selector.select()for key, mask in ready:call_back = key.datacall_back(key)if __name__ == '__main__':fet = Fetcher()fet.get_url('www.xiaohuar.com')loop_forever()

  

转载于:https://www.cnblogs.com/Miracle-boy/p/10004684.html

Python:通过一个小案例深入理解IO多路复用相关推荐

  1. 爬虫的一个小案例:python实现英汉互译

    什么是网络爬虫? 网络爬虫又称网络蜘蛛,是指按照某种规则在网络上爬取所需内容的脚本程序.众所周知,每个网页通常包含其他网页的入口,网络爬虫则通过一个网址依次进入其他网址获取所需内容. 一个小案例:py ...

  2. 一个小案例理解case穿透、switch语句

    一.用case穿透简化代码以一个小案例为例:键盘录入星期数,输出工作日.休息日(1-5)工作日,(6-7)休息日 package test;import java.util.Scanner;publi ...

  3. 怎么用python画房子_用python画一个小房子

    用python画一个小房子 2020年07月22日 | 萬仟网IT编程 | 我要评论 如何用python画一个小房子?效果图如下:代码如下:import turtle# 前置p = turtle.Pe ...

  4. python写游戏脚本-使用Python写一个小游戏

    引言 最近python语言大火,除了在科学计算领域python有用武之地之外,在游戏.后台等方面,python也大放异彩,本篇博文将按照正规的项目开发流程,手把手教大家写个python小游戏,来感受下 ...

  5. python编程小游戏-使用Python写一个小游戏

    引言 最近python语言大火,除了在科学计算领域python有用武之地之外,在游戏.后台等方面,python也大放异彩,本篇博文将按照正规的项目开发流程,手把手教大家写个python小游戏,来感受下 ...

  6. 使用python制作聊天框解谜游戏_使用Python写一个小游戏alien invasion!

    最近python语言大火,除了在科学计算领域python有用武之地之外,在游戏.后台等方面,python也大放异彩,本篇博文将按照正规的项目开发流程,手把手教大家写个python小游戏,来感受下其中的 ...

  7. 手机版python3h如何自制游戏_教你如何用 Python 写一个小游戏

    教你如何用 Python 写一个小游戏 引言 最近 python 语言大火, 除了在科学计算领域 python 有用武之地之外, 在游戏后台等方面, python 也大放异彩, 本篇博文将按照正规的项 ...

  8. 关于python小游戏的毕业论文_使用Python写一个小游戏

    引言 最近python语言大火,除了在科学计算领域python有用武之地之外,在游戏.后台等方面,python也大放异彩,本篇博文将按照正规的项目开发流程,手把手教大家写个python小游戏,来感受下 ...

  9. 利用python做一个小游戏_如何使用python做一个简单的猜数字的小游戏

    1 首先小编先打开IDLE,如下图: 2 然后这里点击菜单栏的'File',然后点击菜单"New File",如下图: 3 然后我们就在idle中新建了一个python文件,如下图 ...

最新文章

  1. 对象属性的调用_PyQt5面向对象编程,类和类的继承
  2. 如何优化页面的响应速度 以及如何减少项目初次加载时间(转https://www.cnblogs.com/MarcoHan/p/5295398.html)...
  3. LeetCode 559N叉树的最大深度-简单
  4. java 转dwg转图片_dwg转pdf要怎么转换
  5. webloigc12服务启动不了_weblogic启动不能锁定AdminServer.lok的故障处理
  6. mysql中字符串和数字的互转函数
  7. 百度免费开放长语音识别功能
  8. ❀❀ 名词解释(PM、PD、UE、UI、RD....)
  9. ANSYS Workbench16教程学习01
  10. 计算机房装修对门的要求,机房建设标准要求
  11. androidgoogle服务包的下载地址
  12. ARP防火墙单机个人版 “此版本已过期,请下载最新版”
  13. php网站视频播放外链,用视频网站来做外链只需三步
  14. thinkphp集成系列之阿里云oss
  15. Entry name ‘META-INF/androidx.vectordrawable_vectordrawable.version‘ collided
  16. RT-Thread移植到STM32单片机过程
  17. 短视频动态贴纸的实现思路
  18. java实训感想6000字_JAVA论文6000字:无线校园
  19. htc hd2刷android,真正的刷机之王! HTC HD2成功刷入安卓7.0
  20. 上海众生无耻的IDC

热门文章

  1. 2022-2028年中国场景金融行业深度调研及投资前景预测报告
  2. 桌子上有个盘子_日本留学生活:留学生在餐厅刷盘子的传闻,竟然在自己身上上演...
  3. 2022-2028年中国金属制品行业投资分析及前景预测报告
  4. Go 源码里的这些 //go: 指令,go:linkname 你知道吗?
  5. 常用的高性能 KV 存储 Redis、Memcached、etcd、Zookeeper 区别
  6. Go 学习笔记(76)— Go 标准库 net/http 创建客户端(发送 GET、POST 请求)
  7. Python 多线程总结(2)— 线程锁、线程池、线程数量、互斥锁、死锁、线程同步
  8. 【Sql Server】DateBase-视频总结
  9. 基于javaGUI的文档识别工具制作
  10. LeetCode简单题之交替合并字符串