协程
协程就是告诉Cpython解释器,你不是nb吗,不是搞了个GIL锁吗,那好,我就自己搞成一个线程让你去执行,省去你切换线程的时间,我自己切换比你切换要快很多,避免了很多的开销。
协程的本质就是在单线程下,由用户自己控制一个任务遇到io阻塞了就切换另外一个任务去执行,以此来提升效率。为了实现它,我们需要找寻一种可以同时满足以下条件的解决方案:
- 可以控制多个任务之间的切换,切换之前将任务的状态保存下来,以便重新运行时,可以基于暂停的位置继续执行。
- 作为1的补充:可以检测io操作,在遇到io操作的情况下才发生切换
asyncio☆☆☆☆☆☆
在pyhon3.4的时候推出的,内置模块,不用安装,确保你的Python解释器版本大于3.4
i.直接上代码:
import asyncio
@asyncio.coroutine #表示这不再是一个普通函数,已经升级为可以异步的战斗机了!
def func1():
print(1)
yield from asyncio.sleep(2) #模拟io,生产中换成实际的io
print(2)
@asyncio.coroutine
def func2():
print(3)
yield from asyncio.sleep(2)
print(4)
#把任务放进任务池中
tasks = [
asyncio.ensure_future(func1()),
asyncio.ensure_future(func2()),
]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
ii.async & await关键字
Python3.5才出现,保证你的版本符合要求
本质上和上面用法是一样的,只是替换掉关键字而已
import asyncio
async def func1(): #async替换掉关键字
print(1)
await asyncio.sleep(2) #等待io
print(2)
async def func2():
print(3)
await asyncio.sleep(2)
print(4)
tasks = [
asyncio.ensure_future(func1()),#future对象较为底层,是task的基类
asyncio.ensure_future(func2()),
]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
iii.run方法,task对象
Python3.7才出现,保证你的版本符合要求
run方法包含了
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
最常用的协程方法
import asyncio
async def func1(): # async替换掉关键字
print(1)
await asyncio.sleep(2) # 等待io
print(2)
return "func1"
async def func2():
print(3)
await asyncio.sleep(2)
print(4)
return "func2"
async def main():
tasks = [
asyncio.create_task(func1()), # 必须声名在在异步函数中,因为放在外面没有task对象会报错
asyncio.create_task(func2()),
]
done, pending = await asyncio.wait(tasks, timeout=None) # done:返回结果,pending:返回未完成的协程函数
for i in done:
print(i.result())
print(pending)
# 运行
asyncio.run(main())
若想把tasks对象放在外面,需要修改代码
import asyncio
async def func1(): # async替换掉关键字
print(1)
await asyncio.sleep(2) # 等待io
print(2)
return "func1"
async def func2():
print(3)
await asyncio.sleep(2)
print(4)
return "func2"
tasks = [
func1(),#不能使用task对象,因为还没有创建事件循环loop对象
func2(),
]
#wait方法自动把协程函数创建task对象
done, pending = asyncio.run(asyncio.wait(tasks, timeout=None)) # done:返回结果,pending:返回未完成的协程函数
for i in done:
print(i.result())
print(pending)
iv.有些库不支持asyncio语法,如requests
当我们拿着asyncio模块实行异步爬虫的时候
import asyncio
import requests
urls = [
"http://www.smilenow.top",
"http://www.baidu.com",
"http://www.163.com"
]
async def get_cont(url):
print("准备下载:", url)
htm = requests.get(url=url).text
print(url, "已经下载完毕")
return url
tasks = map(lambda x: get_cont(x), urls)
asyncio.run(asyncio.wait(tasks, timeout=None)) # done:返回结果,pending:返回未完成的协程函数
结果
准备下载: http://www.baidu.com
http://www.baidu.com 已经下载完毕
准备下载: http://www.163.com
http://www.163.com 已经下载完毕
准备下载: http://www.smilenow.top
http://www.smilenow.top 已经下载完毕
什么鬼,根本没有实现异步好吗?怎么办?用线程池替代!
import asyncio
import requests
urls = [
"http://www.smilenow.top",
"http://www.baidu.com",
"http://www.163.com"
]
async def get_cont(url):
print("准备下载:", url)
loop = asyncio.get_event_loop()
future = loop.run_in_executor(None,requests.get,url)#变成多线程方式运行了
await future
print(url, "已经下载完毕")
return url
tasks = map(lambda x: get_cont(x), urls)
asyncio.run(asyncio.wait(tasks, timeout=None)) # done:返回结果,pending:返回未完成的协程函数
v.asyncio 异步操作redis
下载支持异步的redis模块
# pip install aioredis
import aioredis
import asyncio
class Redis:
_redis = None
async def get_redis_pool(self, *args, **kwargs):
if not self._redis:
self._redis = await aioredis.create_redis_pool(*args, **kwargs)
return self._redis
async def close(self):
if self._redis:
self._redis.close()
await self._redis.wait_closed()
async def get_value(key):
redis = Redis()
r = await redis.get_redis_pool(('127.0.0.1', 6379), db=7, encoding='utf-8')
value = await r.get(key)
print(f'{key!r}: {value!r}')
await redis.close()
if __name__ == '__main__':
asyncio.run(get_value('key')) # need python3.7
vi.aiomysql异步操作mysql
安装:
# pip install aiomysql
import asyncio
import aiomysql
async def execute():
conn = await aiomysql.connect(host='localhost', port=3306, user="root", password='123', db='my')
cur = await conn.cursor()
await cur.excute("select * from user")
result = await cur.fetchall()
print(result)
await cur.close()
await conn.close()
asyncio.run(execute())
v.不够快,uvloop让速度飞翔!!!
uvloop 使得 asyncio 更快. 实际上,比nodejs,gevent,以及其他任何Python异步框架至少快两倍 .uvloop asyncio 基于性能的测试接近于Go程序.
这是一个被各大框架青睐的模块
安装:pip install uvloop
import asyncio
import uvloop
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
#下面正常书写asyncio代码
————————————————
版权声明:本文为CSDN博主「卢政孝simi」的原创文章
原文链接:https://blog.csdn.net/qq_40837794/article/details/109708891