当前位置:   article > 正文

python3 asyncio原理_从零开始学asyncio(中)

python3 asyncio原理_从零开始学asyncio(中)

本篇文章主要是讲解asyncio模块的实现原理. 这个系列还有另外两篇文章:

一. asyncio模块简介

asyncio是python3.4开始内置的一个标准库, 可以用于编写异步的并发代码, 因此非常适合用在IO密集型操作.

现在运行如下代码:

importasyncioimporttime

asyncdeftask(i):print('task{} start at {}'.format(i, time.ctime()))#asyncio.sleep的效果与time.sleep类似, 让程序睡眠n秒

await asyncio.sleep(3)print('task{} end at {}'.format(i, time.ctime()))

tasks= asyncio.wait([task(i) for i in range(3)])

asyncio.run(tasks)

运行结果如下:

三个任务实际是处于同一线程的, 但它们的执行顺序不是start->end->start->end这种串行模式, 而是几乎同时开始, 同时结束, asyncio模块的作用就是, 使用异步的方式实现单线程并发的效果. 最简单的使用步骤如下:

首先, 在定义函数的时候使用关键字async, 这个函数就不是个普通函数了, 调用的时候不会执行内部代码, 而是返回一个coroutine对象, 即协程, 这一点与生成器函数类似.

然后, 在协程函数中的耗时操作前面加上await关键字, 注意await后面必须是可等待对象, 比如asyncio.sleep(n), 可等待对象在本文的第二节有详细的讲解.

最后, 调用asyncio.wait将协程列表打包, 打包结果给asyncio.run运行即可.

二. asyncio实现原理

要理解asyncio的原理, 需要理解如下几个概念: 协程, 事件循环, future/task. 其中协程就是用户自己定义的任务, 事件循环负责监听事件和回调, future/task则主要负责管理回调, 以及驱动协程.

1. 事件循环

事件循环负责同时对多个事件进行监听, 当监听到事件时, 就调用对应的回调函数, 进而驱动不同的任务. 上一节代码最后的asyncio.run, 其本质就是创建一个事件循环, 然后一直运行事件循环, 直到所有任务结束为止.

首先看看上篇文章最后的爬虫代码:

importselectimportsocketimporttime

req= 'GET / HTTP/1.0\r\nHost:cn.bing.com\r\n\r\n'.encode('utf8')

address= ('cn.bing.com', 80)

db=[]classGenCrawler:'''这里使用一个类将生成器封装起来,如果要驱动生成器,就调用next_step方法

另外,这个类还可以获取到使用的socket对象'''

def __init__(self):

self.sock=socket.socket()

self.sock.setblocking(0)

self._gen=self._crawler()defnext_step(self):

next(self._gen)def_crawler(self):

self.sock.connect_ex(address)yieldself.sock.send(req)

response= b''

while 1:yieldchunk= self.sock.recv(1024)if chunk == b'':

self.sock.close()break

else:

response+=chunk

db.append(response)defevent_loop(crawlers):#首先,建立sock与crawler对象的映射关系,便于由socket对象找到对应的crawler对象

#建立映射的同时顺便调用crawler的next_step方法,让内部的生成器运行起来

sock_to_crawler ={}for crawler incrawlers:

sock_to_crawler[crawler.sock]=crawler

crawler.next_step()#select.select需要传入三个列表,分别对应要监听的可读,可写和错误事件的socket对象集合

readable =[]

writeable= [crawler.sock for crawler incrawlers]

errors=[]while 1:

rs, ws, es=select.select(readable, writeable, errors)for sock inws:#当socket对象连接到服务器时,会创建可读缓冲区和可写缓冲区

#由于可写缓冲区创建时为空,因此连接成功时,就触发可写事

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/102604
推荐阅读
相关标签
  

闽ICP备14008679号