【python】协程
2021-04-20 00:15
633 查看
关于python协程这个知识点,我是在研究locust时候发现的。 locust是一款开源的性能测试工具,单机并发能力要比jmeter高,它的并发实现就是通过python协程去实现的。
说到并发,我猜你很容易想到的是多线程,其实协程也是实现并发的一种方式,只不过协程是单线程。
先上一段代码,假设我们在写爬虫抓取4个url的内容,并且我们让每一个url都停留一定的秒数,第一个1s, 第二个停2s,以此类推。所以,可以知道4个url爬取完毕一共要花费10s的时间。
import time def crawl_page(url): print('正在爬取 {}'.format(url)) sleep_time = int(url.split('_')[-1]) time.sleep(sleep_time) print('OK {}'.format(url)) def main(urls): time_start = time.time() for url in urls: crawl_page(url) time_end = time.time() print("爬取耗时共计{}s".format(int(time_end - time_start))) main(['url_1', 'url_2', 'url_3', 'url_4'])
运行一下:
D:\Programs\Python\Python39\python.exe D:/练习/xc.py 正在爬取 url_1 OK url_1 正在爬取 url_2 OK url_2 正在爬取 url_3 OK url_3 正在爬取 url_4 OK url_4 爬取耗时共计10s Process finished with exit code 0
果然运行了10s,可以发现我们在等待中浪费了很多时间,那么如何提高这段代码的运行效率呢? 没错,协程该出场了。
一、async和await
目前,python3.7以上的版本提供了最新的写法,是基于
asyncio和
async / await的方法,所以如果下面 的代码你执行不起来,检查一下是不是你的python版本不对。
import asyncio import time async def crawl_page(url): print('正在爬取 {}'.format(url)) sleep_time = int(url.split('_')[-1]) await asyncio.sleep(sleep_time) print('OK {}'.format(url)) async def main(urls): time_start = time.time() for url in urls: await crawl_page(url) time_end = time.time() print("爬取耗时共计{}s".format(int(time_end - time_start))) asyncio.run(main(['url_1', 'url_2', 'url_3', 'url_4']))
- async:声明这是一个异步函数。调用异步函数,就会得到一个协程对象。
- await:用于调用函数。注意,这里await 执行的效果,和 Python 正常执行是一样的。 也就是说程序会阻塞在这里,进入被调用的协程函数,执行完毕返回后再继续。
- asyncio.run:触发运行。这个函数是 Python 3.7 之后才有的特性,目的就是让Python 的协程接口变得非常简单。
通常情况下,
asyncio.run(main())
作为主程序的入口函数,在程序运行周期内,只调用一次asyncio.run
。
二、asyncio.create_task
上述代码其实还没完成,不信你执行一下,会发现还是10s。这正如上面await介绍说的,其实跟python正常执行一次没区别。 所以,为了实现协程,还需要
asyncio.create_task来创建任务。
import asyncio import time async def crawl_page(url): print('正在爬取 {}'.format(url)) sleep_time = int(url.split('_')[-1]) await asyncio.sleep(sleep_time) print('OK {}'.format(url)) async def main(urls): time_start = time.time() tasks = [] for url in urls: tasks.append(asyncio.create_task(crawl_page(url))) # 创建task for task in tasks: await task time_end = time.time() print("爬取耗时共计{}s".format(int(time_end - time_start))) asyncio.run(main(['url_1', 'url_2', 'url_3', 'url_4']))
执行一下:
D:\Programs\Python\Python39\python.exe D:/练习/xc.py 正在爬取 url_1 正在爬取 url_2 正在爬取 url_3 正在爬取 url_4 OK url_1 OK url_2 OK url_3 OK url_4 爬取耗时共计4s Process finished with exit code 0
这下仅需要4s,也就是4个任务中,耗时最长的那个任务所需要的时间。 执行过程:
- 在第一个for循环里,通过调用异步函数
crawl_page()
得到4个协程对象。 - 在第二个for循环里,通过
asyncio.create_task
给4个协程对象分别创建了task。 - 接着,4个任务被
await
调用执行。
是不是很简单?再次印证了python哲学:简单胜过复杂。
最近可能会用到locust对项目进行性能测试,若有收获,届时分享。
相关文章推荐
- #python 线程,协程
- Python学习总结笔记(7)-- 生成器与协程
- Python全栈(四)高级编程技巧之10.Python多任务-协程
- python web 异步 协程
- python中的协程深入理解
- python修炼——协程and正则表达式!
- python高级编程语法——多任务:协程(三)
- Python协程之greenlet
- Python之路【第七篇】:线程、进程和协程
- python3.6 多进程+协程的配合 提升爬虫效率?
- Python 进程、协程
- python-12-进程,线程,协程
- Python 简易的异步协程使用方法
- 协程的作用 Python
- Python 协程
- python详解(9)--python中的多任务(协程与迭代器和生成器)
- python协程用法实例分析
- Python学习之协程使用同步锁lock方法详解
- Python_oldboy_自动化运维之路_线程,进程,协程(十一)
- python里协程使用同步锁Lock