这里的异步编程基于python3.4和python3.5
1、一些重要的概念理解
(1)循环消息队列:
- 异步IO采用消息循环的模式,重复“读取消息—处理消息”的过程
- 消息模型解决等待IO操作的问题:
- 程序发出IO请求,直接结束本轮消息处理,进入下一轮消息的处理
- 当IO操作完成守,将收到一条IO完成的消息,处理该消息时获取IO操作的结果
- 在IO操作的这段时间里,异步模型可以循环处理其他操作,而且没有线程切换的消耗,同时处理多个IO请求,适用于大多数IO密集型的应用程序
- 总结; "异步IO模型"需要一个消息循环,在消息循环中,主线程不断地重复“读取消息-处理消息”这一过程。
- (2)协程——coroutine
- 协程,又称微线程,纤程。英文名Coroutine。协程的概念很早就提出来了,但直到最近几年才在某些语言(如Lua)中得到广泛应用。子程序,或者称为函数,在所有语言中都是层级调用,比如A调用B,B在执行过程中又调用了C,C执行完毕返回,B执行完毕返回,最后是A执行完毕。
- 协程调用与函数调用的区别:
- 即函数)内部可中断,然后转而执行别的子程序(另一个函数),在适当的时候再返回 来接着执行。
协程针对的是一个线程中的函数调用之间,所以没有线程切换,是在一个线程中轮流执行和终端多个函数而已,所 以效率较高,而且不需要锁机制(只有一个线程执行)
需要注意的是,子程序内部中断的不是函数调用,而是被调用函数中断(一般来说可能是一条执行命令需要很长时 间等待结果返回,比如常见的IO操作),转而去执行另一个函数(不是调用另一个函数),类似两个函数轮流执 行,没有发生函数调用。 - 函数调用:程序执行中,函数调用是通过函数栈实现,因为栈FILO的特点,通常函数调用过程都是:A调用B,B在执行过程中 又调用了C,C执行完毕返回,B执行完毕返回,最后是A执行完毕。子程序(函数)调用总是一个入口,一次返 回,调用顺序是明确的。而协程的调用和子程序不同
- 总结:协程的本质就是一个子程序,即一个使用async关键字定义的函数(在python3.4中使用的是@async.corontine修饰的函 数),它的调用不会立即执行函数,而是会返回一个协程对象。协程对象需要注册到事件循环,由事件循环调用
- (3)、事件循环
- python异步IO的核心就在于事件循环,事件循环是一个无限的循环,程序开启一个无限的循环,程序员会把一些协程函数注册到事件循环上。当满足事件发生的时候,调用相应的协程函数。即协程函数不是自己调用的,而是通过“事件循环”去掉用的。
- 操作如下:
- loop = asyncio.get_event_loop() #获取“事件循环”对象
- loop.run_until_complete(hello()) #通过事件循环,去调用协程函数
- loop.close()
- (4)、任务task
- 协程对象就是一个原生可以挂起的函数,任务则是对协程进一步封装,其中包含任务的各种状态。即多个coroutine函数可以封装成一组Task然后并发执行,所谓task对象是Future类的子类。保存了协程运行后的状态,用于未来获取协程的结果。任务一般是有多个协程函数的时候,将他们绑定到一个任务组上,可以如下操作:
loop = asyncio.get_event_loop()
tasks = [hello(), hello1(),hello2(),hello3()] #多个协程函数绑定到一个任务上面,记住多个任务需要将任务写成 列表的形式
loop.run_until_complete(asyncio.wait(tasks)) #当任务是列表的时候,必须使用asyncio.wait(t)的形式才行
loop.close()
- loop.run_until_complete(hello()) #程序也会自动将hello封装成一个task任务,若函数有参数,一定要传入参数哦
- 下面是几种创建任务的方法:
- 方法1:通过loop.create_task()创建任务
• =[loop.create_task(number_sub(
10))] #因为是列表的形式,所以下面必须要用asyncio.wait(t)作为参数,否则会报错
loop.run_until_complete(asyncio.wait(t))
loop.close()
- 与下面的等价
=loop.create_task(number_sub(
10)) #因为不是列表的形式,所以不需要
asyncio.wait,而直接将t作为参数,否则会报错
loop.close()
方法2:通过 asyncio.ensure_future()创建任务
=[asyncio.ensure_future(number_sub(
10))]
#因为是列表的形式,所以下面必须要用asyncio.wait(t)作为参数,否则会报错
loop.close()
=asyncio.ensure_future(number_sub(
10))
#因为不是列表的形式,所以不需要
asyncio.wait,而直接将t作为参数,否则会报错
loop.close()
方法3:一次将多个协程函数绑定到同一个任务——要写成任务列表的形式
#t=[asyncio.ensure_future(number_sub(10)),asyncio.ensure_future(character_list())] #方法一
=[loop.create_task(number_sub(
10)),loop.create_task(character_list())] #方法二
=[number_sub(
10),character_list()] #方法三,这三种方法都可以
loop.run_until_complete(asyncio.wait(t)) #但因为是列表形式,所以要用wait
loop.close()
=asyncio.gather(asyncio.ensure_future(number_sub(
5)), asyncio.ensure_future(character_list()))
print(t)
loop.run_until_complete(t) #通过asyncio的gather()方法将多个协程函数连接起来,而没有写成列表形式,故而不需要wait()
print(t)
loop.close()
- task进一步了解:
- task也是一个对象,我们还可以通过打印task,来来查看相关的一些状态信息,如下所示:
• =asyncio.get_event_loop()
=loop.create_task(number_sub(
10))
print(t) #执行之前的状态
loop.run_until_complete(t)
print(t)
loop.close()
<Task pending coro=<number_sub() running at f:\�о������꼶��\Tensorflow���ѧϰ\TensorFlowBoardʵ��\asyncio_3.4.py:3>>
it's 10
it's 9
it's 8
it's 7
it's 6
it's 5
it's 4
it's 3
it's 2
it's 1
<Task finished coro=<number_sub() done, defined at f:\�о������꼶��\Tensorflow���ѧϰ\TensorFlowBoardʵ��\asyncio_3.4.py:3> result=None>
- task是Future的子类。isinstance(task, asyncio.Future)将会输出True。
- 下面是整个程序的代码:
import
@asyncio.coroutine #在python3.5里面,改为了async def number_sub(n):
def
number_sub(n):
while n
>
0:
print(
"it's
{0}
".format(n))
yield
from asyncio.sleep(
1)
#asyncio.sleep()本身就是一个“异步协程函数” 当然这里可以是其他自定义的异步协程函数,
#这里仅仅是模拟一个耗时的异步过程
-=
1 #在python3.5里面,改为了await asyncio.sleep(1)
@asyncio.coroutine #在python3.5里面,改为了async def number_sub(n):
def
character_list():
=[
'a',
'b',
'c',
'd',
'e',
'f',
'g']
for i
in
print(
"it's
{0}
".format(i))
yield
from asyncio.sleep(
1)
#在python3.5里面,改为了await asyncio.sleep(1)
=asyncio.gather(asyncio.ensure_future(number_sub(
5)), asyncio.ensure_future(character_list()))
loop.run_until_complete(t)
loop.close()