超详细的Python中异步协程的使用方法介绍

1.前言在执行一些IO密集型任务的时候,程序常常会因为等待IO而阻塞。比如在网络爬虫中,如果我们使用requests库来进行请求的话,如果网站响应速度过慢,程序一直在等待网站响应,最后导致其爬取效率是非常非常低的。为了解决这类问题,本文就来探讨一下Python中异步协程来加速的方法,此种方法对于IO密集型任务非常有效。如将其应用到网络爬虫中,爬取效率甚至可以成百倍地提升。

2.基本了解在了解异步协程之前,我们首先得了解一些基础概念,如阻塞和非阻塞、同步和异步、多进程和协程。2.1阻塞阻塞状态指程序未得到所需计算资源时被挂起的状态。程序在等待某个操作完成期间,自身无法继续干别的事情,则称该程序在该操作上是阻塞的。常见的阻塞形式有:网络I/O阻塞、磁盘I/O阻塞、用户输入阻塞等。

首先我们引入了asyncio这个包,这样我们才可以使用async和await,然后我们使用async定义了一个execute()方法,方法接收一个数字参数,方法执行之后会打印这个数字。随后我们直接调用了这个方法,然而这个方法并没有执行,而是返回了一个coroutine协程对象。

这里我们定义了loop对象之后,接着调用了它的create_task()方法将coroutine对象转化为了task对象,随后我们打印输出一下,发现它是pending状态。

发现其效果都是一样的。3.2绑定回调另外我们也可以为某个task绑定一个回调方法,来看下面的例子:

在这里我们定义了一个request()方法,请求了百度,返回状态码,但是这个方法里面我们没有任何print()语句。随后我们定义了一个callback()方法,这个方法接收一个参数,是task对象,然后调用print()方法打印了task对象的结果。

3.4协程实现前面说了这么一通,又是async,又是coroutine,又是task,又是callback,但似乎并没有看出协程的优势啊?反而写法上更加奇怪和麻烦了,别急,上面的案例只是为后面的使用作铺垫,接下来我们正式来看下协程在解决IO密集型任务上有怎样的优势吧!上面的代码中,我们用一个网络请求作为示例,这就是一个耗时等待的操作,因为我们请求网页之后需要等待页面响应并返回结果。

这里我们定义了一个Flask服务,主入口是index()方法,方法里面先调用了sleep()方法休眠3秒,然后接着再返回结果,也就是说,每次请求这个接口至少要耗时3秒,这样我们就模拟了一个慢速的服务接口。注意这里服务启动的时候,run()方法加了一个参数threaded,这表明Flask启动了多线程模式,不然默认是只有一个线程的。

可以发现和正常的请求并没有什么两样,依然还是顺次执行的,耗时15秒,平均一个请求耗时3秒,说好的异步处理呢?其实,要实现异步处理,我们得先要有挂起的操作,当一个任务需要等待IO结果的时候,可以挂起当前任务,转而去执行其他任务,这样我们才能充分利用好资源,上面方法都是一本正经的串行走下来,连个挂起都没有,怎么可能实现异步?想太多了。

这次它遇到await方法确实挂起了,也等待了,但是最后却报了这么个错,这个错误的意思是requests返回的Response对象不能和await一起使用,为什么呢?

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180723A1IV7P00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券