asyncio使用

  1. import asyncio
  2. import time
  3. async def add(x=1,y=2):
  4. print("Add {} + {} ... ".format(x,y))
  5. await asyncio.sleep(2)
  6. return x+y
  7. s=time.time()
  8. loop = asyncio.get_event_loop()
  9. loop.run_until_complete(add())
  10. print('cost:', time.time()-s)

运行结果

  1. Add 1 + 2 ...
  2. cost: 2.0028231143951416
  • async就是声明一个函数为协程。而await就是代替原来的yield from。
  • 协程里面是不能有阻塞操作的,对于sleep这样的阻塞我们需要用关键是await来包装一下,可以理解为执行一个sleep的协程,sleep 2秒
  • 协程需要搭配事件循环才能使用,用asyncio库里面的get_event_loop声明一个异步的事件循环
  • 然后把我们的add函数注册到loop中去
  • 最后我们等待事件完成run_until_complete

    执行多个task

    ```python import asyncio import time

async def add(x=1,y=2): print(“Add {} + {} … “.format(x,y)) await asyncio.sleep(2) return x+y s=time.time() loop = asyncio.get_event_loop() task = [add(x,y) for x,y in zip(range(1,10),range(11,10))] loop.run_until_complete(asyncio.wait(tasks)) print(‘cost:’, time.time()-s)

  1. 运行结果
  2. ```python
  3. Add 2 + 12 ...
  4. Add 4 + 14 ...
  5. Add 6 + 16 ...
  6. Add 5 + 15 ...
  7. Add 3 + 13 ...
  8. Add 1 + 11 ...
  9. Add 8 + 18 ...
  10. Add 9 + 19 ...
  11. Add 7 + 17 ...
  12. cost: 2.0028231143951416

如果获取多个并发的task的结果

  1. import asyncio
  2. import time
  3. async def add(x=1,y=2):
  4. print("Add {} + {} ... ".format(x,y))
  5. await asyncio.sleep(2)
  6. return x+y
  7. s=time.time()
  8. loop = asyncio.get_event_loop()
  9. tasks = [loop.create_task(add(1,2)),loop.create_task(add(11,12)),loop.create_task(add(111,112))]
  10. loop.run_until_complete(asyncio.wait(tasks))
  11. for task in tasks:
  12. print(task.result())
  13. print('cost',time.time()-s)
  • 如果我们要获取task的结果,一定要创建一个task, 就是把我们的协程绑定要task上,这里直接用事件循环loop里面的create_task就可以搞定。
  • 我们假设有3个并发的add任务需要处理,然后调用run_until_complete来等待3个并发任务完成。
  • 调用task.result查看结果,这里的task其实是_asyncio.Task,是封装的一个类。

    asyncio的方法

  • asyncio.get_event_loop() 声明一个异步的事件循环,主线程调用asyncio.get_event_loop()时会创建事件循环,你需要把异步的任务丢给这个循环的run_until_complete()方法

  • asyncio.wait() 声明一个任务列表
  • asyncio.sleep() 一个sleep的协程

    aiohttp

    并发http请求,from aiohttp import ClientSession,首先要建立一个session对象,然后用session对象去打开网页。session可以进行多项操作,比如post, get, put, head等。
  1. async with ClientSession() as session:
  2. async with session.get(url) as response:

例子

  1. import asyncio
  2. from aiohttp import ClientSession
  3. tasks = []
  4. url = "https://www.baidu.com/{}"
  5. async def hello(url):
  6. async with ClientSession() as session:
  7. async with session.get(url) as response:
  8. response = await response.read()
  9. print(response)
  10. if __name__ == '__main__':
  11. loop = asyncio.get_event_loop()
  12. loop.run_until_complete(hello(url))

首先async def 关键字定义了这是个异步函数,await 关键字加在需要等待的操作前面,response.read()等待request响应,是个耗IO操作。然后使用ClientSession类发起http请求。

多链接异步访问

  1. import time
  2. import asyncio
  3. from aiohttp import ClientSession
  4. tasks = []
  5. url = "https://www.baidu.com/{}"
  6. async def hello(url):
  7. async with ClientSession() as session:
  8. async with session.get(url) as response:
  9. response = await response.read()
  10. # print(response)
  11. print('Hello World:%s' % time.time())
  12. def run():
  13. # 将hello()包装在asyncio的Future对象中,然后将Future对象列表作为任务传递给事件循环
  14. for i in range(5):
  15. task = asyncio.ensure_future(hello(url.format(i)))
  16. tasks.append(task)
  17. if __name__ == '__main__':
  18. loop = asyncio.get_event_loop()
  19. run()
  20. loop.run_until_complete(asyncio.wait(tasks))

ensure_future可将协程封装成Task

收集http响应
可通过asyncio.gather(*tasks)将响应全部收集起来

  1. import time
  2. import asyncio
  3. from aiohttp import ClientSession
  4. tasks = []
  5. url = "https://www.baidu.com/{}"
  6. async def hello(url):
  7. async with ClientSession() as session:
  8. async with session.get(url) as response:
  9. # print(response)
  10. print('Hello World:%s' % time.time())
  11. return await response.read()
  12. def run():
  13. for i in range(5):
  14. task = asyncio.ensure_future(hello(url.format(i)))
  15. tasks.append(task)
  16. result = loop.run_until_complete(asyncio.gather(*tasks))
  17. print(result)
  18. if __name__ == '__main__':
  19. loop = asyncio.get_event_loop()
  20. run()

输出

  1. Hello World:1527765369.0785167
  2. Hello World:1527765369.0845182
  3. Hello World:1527765369.0910277
  4. Hello World:1527765369.0920424
  5. Hello World:1527765369.097017
  6. [b'<!DOCTYPE html>\r\n<!--STATUS OK-->\r\n<html>\r\n<head>\r\n......

异常解决

  1. 限制并发数量
  2. 使用回调方式
  3. 增大文件描述符

    python的async原理

    asyncio可以实现单线程并发IO操作。如果仅用在客户端,发挥的威力不大。如果把asyncio用在服务器端,例如Web服务器,由于HTTP连接就是IO操作,因此可以用单线程+coroutine实现多用户的高并发支持
    一旦遇到io,就会从用户态控制切换,以此来提升效率