asyncio 是干什么的?

异步网络操作

并发

协程

python3.0 时代,标准库里的异步网络模块:select(非常底层) python3.0时代,第三方异步网络库:Tornado

python3.4时代,asyncio:支持 TCP ,子进程

现在的asyncio,有了很多的模块已经在支持:aiohttp,aiodns,aioredis等等 https://github.com/aio-libs 这里列出了已经支持的内容,并在持续更新

当然到目前为止实现协程的不仅仅只有asyncio模块,tornado和gevent都实现了类似功能

关于asyncio的一些关键字的说明:

event_loop 事件循环:程序开启一个无限循环,把一些函数注册到事件循环上,当满足事件发生的时候,调用相应的协程函数

coroutine 协程:协程对象,指一个使用async关键字定义的函数,它的调用不会立即执行函数,而是会返回一个协程对象。协程对象需要注册到事件循环,由事件循环调用。

task 任务:一个协程对象就是一个原生可以挂起的函数,任务则是对协程进一步封装,其中包含了任务的各种状态

future: 代表将来执行或没有执行的任务的结果。它和task上没有本质上的区别

async/await 关键字:python3.5用于定义协程的关键字,async定义一个协程,await用于挂起阻塞的异步调用接口。

定义一个协程

import time

import asyncio

now = lambda : time.time()

async def do_some_work(x):

print("waiting:", x)

start = now()

# 这里是一个协程对象,这个时候do_some_work函数并没有执行

coroutine = do_some_work(2)

print(coroutine)

# 创建一个事件loop

loop = asyncio.get_event_loop()

# 将协程加入到事件循环loop

loop.run_until_complete(coroutine)

print("Time:",now()-start)

在上面带中我们通过async关键字定义一个协程(coroutine),当然协程不能直接运行,需要将协程加入到事件循环loop中

asyncio.get_event_loop:创建一个事件循环,然后使用run_until_complete将协程注册到事件循环,并启动事件循环

创建一个task

协程对象不能直接运行,在注册事件循环的时候,其实是run_until_complete方法将协程包装成为了一个任务(task)对象. task对象是Future类的子类,保存了协程运行后的状态,用于未来获取协程的结果

import asyncio

import time

now = lambda: time.time()

async def do_some_work(x):

print("waiting:", x)

start = now()

coroutine = do_some_work(2)

loop = asyncio.get_event_loop()

task = loop.create_task(coroutine)

print(task)

loop.run_until_complete(task)

print(task)

print("Time:",now()-start)

结果:

>

waiting: 2

result=None>

Time: 0.0003514289855957031

创建task后,在task加入事件循环之前为pending状态,当完成后,状态为finished

关于上面通过loop.create_task(coroutine)创建task,同样的可以通过 asyncio.ensure_future(coroutine)创建task

asyncio.ensure_future(coro_or_future, *, loop=None)¶

Schedule the execution of a coroutine object: wrap it in a future. Return a Task object.

If the argument is a Future, it is returned directly.

绑定回调

绑定回调,在task执行完成的时候可以获取执行的结果,回调的最后一个参数是future对象,通过该对象可以获取协程返回值。

import time

import asyncio

now = lambda : time.time()

async def do_some_work(x):

print("waiting:",x)

return "Done after {}s".format(x)

def callback(future):

print("callback:",future.result())

start = now()

coroutine = do_some_work(2)

loop = asyncio.get_event_loop()

task = asyncio.ensure_future(coroutine)

print(task)

task.add_done_callback(callback)

print(task)

loop.run_until_complete(task)

print("Time:", now()-start)

结果为:

>

cb=[callback() at /app/py_code/study_asyncio/simple_ex3.py:18]>

waiting: 2

callback: Done after 2s

Time: 0.00039196014404296875

通过add_done_callback方法给task任务添加回调函数,当task(也可以说是coroutine)执行完成的时候,就会调用回调函数。并通过参数future获取协程执行的结果。这里我们创建 的task和回调里的future对象实际上是同一个对象

阻塞和await

使用async可以定义协程对象,使用await可以针对耗时的操作进行挂起,就像生成器里的yield一样,函数让出控制权。协程遇到await,事件循环将会挂起该协程,执行别的协程,直到其他的协程也挂起或者执行完毕,再进行下一个协程的执行

耗时的操作一般是一些IO操作,例如网络请求,文件读取等。我们使用asyncio.sleep函数来模拟IO操作。协程的目的也是让这些IO操作异步化。

import asyncio

import time

now = lambda :time.time()

async def do_some_work(x):

print("waiting:",x)

# await 后面就是调用耗时的操作

await asyncio.sleep(x)

return "Done after {}s".format(x)

start = now()

coroutine = do_some_work(2)

loop = asyncio.get_event_loop()

task = asyncio.ensure_future(coroutine)

loop.run_until_complete(task)

print("Task ret:", task.result())

print("Time:", now() - start)

结果:

waiting: 2

Task ret: Done after 2s

Time: 2.012403726577759

在await asyncio.sleep(x),因为这里sleep了,模拟了阻塞或者耗时操作,这个时候就会让出控制权。 即当遇到阻塞调用的函数的时候,使用await方法将协程的控制权让出,以便loop调用其他的协程。

并发和并行

并发指的是同时具有多个活动的系统

并行指的是用并发来使一个系统运行的更快。并行可以在操作系统的多个抽象层次进行运用

所以并发通常是指有多个任务需要同时进行,并行则是同一个时刻有多个任务执行

下面这个例子非常形象:

并发情况下是一个老师在同一时间段辅助不同的人功课。并行则是指多个老师分别同时辅助多个学生功课。简而言之就是一个人同时吃三个馒头还是三个人每人同时吃一个的情况,吃一个馒头算一个任务

使用asyncio实现并发

import asyncio

import time

now = lambda :time.time()

async def do_some_work(x):

print("Waiting:",x)

await asyncio.sleep(x)

return "Done after {}s".format(x)

start = now()

coroutine1 = do_some_work(1)

coroutine2 = do_some_work(2)

coroutine3 = do_some_work(4)

tasks = [

asyncio.ensure_future(coroutine1),

asyncio.ensure_future(coroutine2),

asyncio.ensure_future(coroutine3)

]

loop = asyncio.get_event_loop()

loop.run_until_complete(asyncio.wait(tasks))

for task in tasks:

print("Task ret:",task.result())

print("Time:",now()-start)

运行结果:

Waiting: 1

Waiting: 2

Waiting: 4

Task ret: Done after 1s

Task ret: Done after 2s

Task ret: Done after 4s

Time: 4.004154920578003

总时间为4s左右。4s的阻塞时间,足够前面两个协程执行完毕。如果是同步顺序的任务,那么至少需要7s。此时我们使用了aysncio实现了并发。asyncio.wait(tasks) 也可以使用 asyncio.gather(*tasks) ,前者接受一个task列表,后者接收一堆task。

关于asyncio.gather和asyncio.wait官网的说明:

asyncio.gather

Return a future aggregating results from the given coroutine objects or futures.

All futures must share the same event loop. If all the tasks are done successfully, the returned future’s result is the list of results (in the order of the original sequence, not necessarily the order of results arrival). If return_exceptions is true, exceptions in the tasks are treated the same as successful results, and gathered in the result list; otherwise, the first raised exception will be immediately propagated to the returned future.

asyncio.wait

Wait for the Futures and coroutine objects given by the sequence futures to complete. Coroutines will be wrapped in Tasks. Returns two sets of Future: (done, pending).

The sequence futures must not be empty.

timeout can be used to control the maximum number of seconds to wait before returning. timeout can be an int or float. If timeout is not specified or None, there is no limit to the wait time.

return_when indicates when this function should return.

协程嵌套

使用async可以定义协程,协程用于耗时的io操作,我们也可以封装更多的io操作过程,这样就实现了嵌套的协程,即一个协程中await了另外一个协程,如此连接起来。

import asyncio

import time

now = lambda: time.time()

async def do_some_work(x):

print("waiting:",x)

await asyncio.sleep(x)

return "Done after {}s".format(x)

async def main():

coroutine1 = do_some_work(1)

coroutine2 = do_some_work(2)

coroutine3 = do_some_work(4)

tasks = [

asyncio.ensure_future(coroutine1),

asyncio.ensure_future(coroutine2),

asyncio.ensure_future(coroutine3)

]

dones, pendings = await asyncio.wait(tasks)

for task in dones:

print("Task ret:", task.result())

# results = await asyncio.gather(*tasks)

# for result in results:

# print("Task ret:",result)

start = now()

loop = asyncio.get_event_loop()

loop.run_until_complete(main())

print("Time:", now()-start)

结果:

waiting: 1

waiting: 2

waiting: 4

Task ret: Done after 1s

Task ret: Done after 2s

Task ret: Done after 4s

Time: 4.02480673789978

如果把上面代码中的:

dones, pendings = await asyncio.wait(tasks)

for task in dones:

print("Task ret:", task.result())

替换为:

results = await asyncio.gather(*tasks)

for result in results:

print("Task ret:",result)

这样得到的就是一个结果的列表

不在main协程函数里处理结果,直接返回await的内容,那么最外层的run_until_complete将会返回main协程的结果。 将上述的代码更改为:

import asyncio

import time

now = lambda: time.time()

async def do_some_work(x):

print("waiting:",x)

await asyncio.sleep(x)

return "Done after {}s".format(x)

async def main():

coroutine1 = do_some_work(1)

coroutine2 = do_some_work(2)

coroutine3 = do_some_work(4)

tasks = [

asyncio.ensure_future(coroutine1),

asyncio.ensure_future(coroutine2),

asyncio.ensure_future(coroutine3)

]

return await asyncio.gather(*tasks)

start = now()

loop = asyncio.get_event_loop()

results = loop.run_until_complete(main())

for result in results:

print("Task ret:",result)

print("Time:", now()-start)

或者返回使用asyncio.wait方式挂起协程。

将代码更改为:

import asyncio

import time

now = lambda: time.time()

async def do_some_work(x):

print("waiting:",x)

await asyncio.sleep(x)

return "Done after {}s".format(x)

async def main():

coroutine1 = do_some_work(1)

coroutine2 = do_some_work(2)

coroutine3 = do_some_work(4)

tasks = [

asyncio.ensure_future(coroutine1),

asyncio.ensure_future(coroutine2),

asyncio.ensure_future(coroutine3)

]

return await asyncio.wait(tasks)

start = now()

loop = asyncio.get_event_loop()

done,pending = loop.run_until_complete(main())

for task in done:

print("Task ret:",task.result())

print("Time:", now()-start)

也可以使用asyncio的as_completed方法

import asyncio

import time

now = lambda: time.time()

async def do_some_work(x):

print("waiting:",x)

await asyncio.sleep(x)

return "Done after {}s".format(x)

async def main():

coroutine1 = do_some_work(1)

coroutine2 = do_some_work(2)

coroutine3 = do_some_work(4)

tasks = [

asyncio.ensure_future(coroutine1),

asyncio.ensure_future(coroutine2),

asyncio.ensure_future(coroutine3)

]

for task in asyncio.as_completed(tasks):

result = await task

print("Task ret: {}".format(result))

start = now()

loop = asyncio.get_event_loop()

loop.run_until_complete(main())

print("Time:", now()-start)

从上面也可以看出,协程的调用和组合非常灵活,主要体现在对于结果的处理:如何返回,如何挂起

协程的停止

future对象有几个状态:

Pending

Running

Done

Cacelled

创建future的时候,task为pending,事件循环调用执行的时候当然就是running,调用完毕自然就是done,如果需要停止事件循环,就需要先把task取消。可以使用asyncio.Task获取事件循环的task

import asyncio

import time

now = lambda :time.time()

async def do_some_work(x):

print("Waiting:",x)

await asyncio.sleep(x)

return "Done after {}s".format(x)

coroutine1 =do_some_work(1)

coroutine2 =do_some_work(2)

coroutine3 =do_some_work(2)

tasks = [

asyncio.ensure_future(coroutine1),

asyncio.ensure_future(coroutine2),

asyncio.ensure_future(coroutine3),

]

start = now()

loop = asyncio.get_event_loop()

try:

loop.run_until_complete(asyncio.wait(tasks))

except KeyboardInterrupt as e:

print(asyncio.Task.all_tasks())

for task in asyncio.Task.all_tasks():

print(task.cancel())

loop.stop()

loop.run_forever()

finally:

loop.close()

print("Time:",now()-start)

启动事件循环之后,马上 ctrl+c,会触发run_until_complete的执行异常 KeyBorardInterrupt。然后通过循环asyncio.Task取消future。可以看到输出如下:

Waiting: 1

Waiting: 2

Waiting: 2

^C{ result='Done after 1s'>, wait_for= cb=[_wait.._on_completion() at /usr/local/lib/python3.5/asyncio/tasks.py:428]>, wait_for= cb=[_wait.._on_completion() at /usr/local/lib/python3.5/asyncio/tasks.py:428]>, wait_for=>}

False

True

True

True

Time: 1.0707225799560547

True表示cannel成功,loop.stop之后还需要再次开启事件循环,最后在close,不然还会抛出异常

循环task,逐个cancel是一种方案,可是正如上面我们把task的列表封装在main函数中,main函数外进行事件循环的调用。这个时候,main相当于最外出的一个task,那么处理包装的main函数即可。

不同线程的事件循环

很多时候,我们的事件循环用于注册协程,而有的协程需要动态的添加到事件循环中。一个简单的方式就是使用多线程。当前线程创建一个事件循环,然后在新建一个线程,在新线程中启动事件循环。当前线程不会被block(阻塞)。

import asyncio

from threading import Thread

import time

now = lambda :time.time()

def start_loop(loop):

asyncio.set_event_loop(loop)

loop.run_forever()

def more_work(x):

print('More work {}'.format(x))

time.sleep(x)

print('Finished more work {}'.format(x))

start = now()

new_loop = asyncio.new_event_loop()

t = Thread(target=start_loop, args=(new_loop,))

t.start()

print('TIME: {}'.format(time.time() - start))

new_loop.call_soon_threadsafe(more_work, 6)

new_loop.call_soon_threadsafe(more_work, 3)

启动上述代码之后,当前线程不会被block,新线程中会按照顺序执行call_soon_threadsafe方法注册的more_work方法, 后者因为time.sleep操作是同步阻塞的,因此运行完毕more_work需要大致6 + 3

新线程协程

import asyncio

import time

from threading import Thread

now = lambda :time.time()

def start_loop(loop):

asyncio.set_event_loop(loop)

loop.run_forever()

async def do_some_work(x):

print('Waiting {}'.format(x))

await asyncio.sleep(x)

print('Done after {}s'.format(x))

def more_work(x):

print('More work {}'.format(x))

time.sleep(x)

print('Finished more work {}'.format(x))

start = now()

new_loop = asyncio.new_event_loop()

t = Thread(target=start_loop, args=(new_loop,))

t.start()

print('TIME: {}'.format(time.time() - start))

asyncio.run_coroutine_threadsafe(do_some_work(6), new_loop)

asyncio.run_coroutine_threadsafe(do_some_work(4), new_loop)

上述的例子,主线程中创建一个new_loop,然后在另外的子线程中开启一个无限事件循环。 主线程通过run_coroutine_threadsafe新注册协程对象。这样就能在子线程中进行事件循环的并发操作,同时主线程又不会被block。一共执行的时间大概在6s左右。

python asyncio_python中asyncio模块相关推荐

  1. python asyncio_Python之asyncio模块的使用

    asyncio模块作用:构建协程并发应用的工具 python并发的三大内置模块,简单认识: 1.multiprocessing:多进程并发处理2.threading模块:多线程并发处理3.asynci ...

  2. python asyncio_Python中asyncio神器的入门

    异步io的优势 传统web应用的一大缺点是,遇到io只能阻塞,等待处理完后再返回,在此期间,无法处理其他的请求.严重降低了系统的并发能力.而异步io则应用而生,本文将从协程.asyncio.aioht ...

  3. Python标准库asyncio模块基本原理浅析

    Python标准库asyncio模块基本原理浅析 本文环境python3.7.0 asyncio模块的实现思路 当前编程语言都开始在语言层面上,开始简化对异步程序的编程过程,其中Python中也开始了 ...

  4. python random模块中的指令_10分钟让你掌握python编程中random模块功能使用,非常详细...

    原标题:10分钟让你掌握python编程中random模块功能使用,非常详细 python作为一门高级编程语言,它的定位是优雅.明确和简单.阅读Python编写的代码感觉像在阅读英语一样,这让使用者可 ...

  5. python bisect_python中bisect模块用法实例

    本文实例讲述了python中bisect模块用法,分享给大家供大家参考. 具体方法分析如下: 这个模块只有几个函数,一旦决定使用二分搜索时,立马要想到使用这个模块. 示例代码如下: import bi ...

  6. python hashlib_python中hashlib模块用法示例

    我们以前介绍过一篇Python加密的文章:Python 加密的实例详解.今天我们看看python中hashlib模块用法示例,具体如下. hashlib hashlib主要提供字符加密功能,将md5和 ...

  7. python configparse_python中ConfigParse模块的用法

    ConfigParser 是Python自带的模块, 用来读写配置文件, 用法及其简单. 配置文件的格式是: [...]包含的叫section section 下有option=value这样的键值 ...

  8. python asyncio_Python 中的异步编程:Asyncio

    异步和同步的代码对比 现在我们实际验证异步模式的切实有效,我会比较两段 python 脚本,这两个脚本除了sleep方法外,其余部分完全相同.在第一个脚本里,我会用标准的 time.sleep 方法, ...

  9. python json模块详解_深入解析Python编程中JSON模块的使用

    JSON编码支持的基本数据类型为 None , bool , int , float 和 str , 以及包含这些类型数据的lists,tuples和dictionaries. 对于dictionar ...

最新文章

  1. [kubernetes] 资源管理 --- 资源预留实践
  2. vue 圆形 水波_vue项目百度地图+echarts的涟漪水波效果
  3. P1574: [Usaco2009 Jan]地震损坏Damage
  4. python之类的封装、多态、继承
  5. 2018 蓝桥杯省赛 B 组模拟赛(五)H题
  6. 已安装jre1.7的情况下安装jdk1.6
  7. c语言void swap1,C语言的一些遗留问题(1):关于交换两个数的swap()函数的一些问题...
  8. 原创拟态UI3.0-一款完全开源的个人主页源码
  9. PIFA 天线的推论及计算方法
  10. 如何安装SQL server2005的查询分析器(绝对原创)
  11. sizeof 32位和64位操作系统的区别
  12. 冯·诺依曼结构:现代计算机的诞生
  13. HDU 1870 愚人节的礼物
  14. 5.1.2 网络编程进阶---开启子进程的两种方式,进程之间内存空间是相互隔离的...
  15. 毕业设计:电子/通信/物联网/计算机专业选题目参考(嵌入式linux/单片机STM32/web/图像)
  16. pytest.fixture如何像testng的beforeMethod一样使用
  17. visual c++ 动态链接库调用总结
  18. 日本Java18_死磕18个Java8日期处理,工作必用!
  19. gpu数据库-mysql_gpu数据库是什么
  20. 基于JAVA实现的幼儿园信息管理系统

热门文章

  1. win10任务栏显示“中/英“语言标识
  2. 重新设置mysql密码
  3. 程序员啊,他又加班了!
  4. 幕布,为知笔记和Effie哪个更适合采编呢?
  5. hc05与单片机连接图_基于proteus的51单片机开发实例37-DAC0832
  6. SQL外键foreign key 表与表之间的三种关系
  7. 山东大学往年c语言期末试题及答案,山东大学历年C语言题库.doc
  8. git cz: Missing script: “test“ To see a list of scripts, run: npm run
  9. pytorch实现手写数字识别_送源码!人工智能实现:识别图片中的手写数字,值得收藏...
  10. NOIp2016 联赛总结——新手上路请多关照