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

本文深入探讨了Python3的asyncio模块,讲解了其工作原理,包括事件循环、协程、future/task的概念。通过示例代码展示了如何使用asyncio实现异步并发,并解释了async/await关键字以及future和task对象在协程执行中的作用,帮助读者理解asyncio的底层机制和使用技巧。
摘要由CSDN通过智能技术生成

本篇文章主要是讲解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对象连接到服务器时,会创建可读缓冲区和可写缓冲区

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值