python 异步io_python---异步IO(asyncio)协程

asyncio.ensure_future(coroutine) 和 loop.create_task(coroutine)都可以创建一个task,run_until_complete的参数是一个futrue对象。当传入一个协程,其内部会自动封装成task,task是Future的子类。isinstance(task, asyncio.Future)将会输出True。

绑定回调add_done_callback

async def func1(num):

print(num,'before---func1----')

return "recv num %s"%num

def callback(future):

print(future.result())

if __name__ == "__main__":

begin = time.time()

coroutine1 = func1(1)

loop = asyncio.get_event_loop()

task1=asyncio.ensure_future(coroutine1)

task1.add_done_callback(callback)

loop.run_until_complete(task1)

loop.close()

end = time.time()

print(end-begin)

1 before---func1----

recv num 1

0.004000186920166016

可以看到,coroutine执行结束时候会调用回调函数。并通过参数future获取协程执行的结果。我们创建的task和回调里的future对象,实际上是同一个对象。

我也可以不使用回调函数,单纯获取返回值

当task状态为finished时候,我们可以直接使用result方法(在future模块)获取返回值

asyncdef func1(num):

print(num,'before---func1----')return "recv num %s"%numif __name__ == "__main__":

begin=time.time()

coroutine1= func1(1)

loop=asyncio.get_event_loop()

task1=asyncio.ensure_future(coroutine1)

loop.run_until_complete(task1)

print(task1)

print(task1.result())

loop.close()

end=time.time()

print(end-begin)

1 before---func1----

result='recv num 1'>recv num1

0.0030002593994140625

阻塞和await

使用async关键字定义的协程对象,使用await可以针对耗时的操作进行挂起(是生成器中的yield的替代,但是本地协程函数不允许使用),让出当前控制权。协程遇到await,事件循环将会挂起该协程,执行别的协程,直到其他协程也挂起,或者执行完毕,在进行下一个协程的执行

使用asyncio.sleep模拟阻塞操作。

import asyncio,timeasyncdef func1(num):

print(num,'before---func1----')await asyncio.sleep(num)return "recv num %s"%numif __name__ == "__main__":

begin=time.time()

coroutine1= func1(5)

coroutine2= func1(3)

loop=asyncio.get_event_loop()

task1=asyncio.ensure_future(coroutine1)

task2=asyncio.ensure_future(coroutine2)

tasks= asyncio.gather(*[task1,task2])    #gather可以实现同时注册多个任务,实现并发操作。wait方法使用一致

loop.run_until_complete(tasks)

loop.close()

end=time.time()

print(end-begin)

1309518-20180624102403769-467800523.png

并发:使用gather或者wait可以同时注册多个任务,实现并发

gather:Return a future aggregating results from the given coroutines or futures.  返回结果

task1=asyncio.ensure_future(coroutine1)

task2=asyncio.ensure_future(coroutine2)

tasks= asyncio.gather(*[task1,task2])loop.run_until_complete(tasks)

wait:Returns two sets of Future: (done, pending).   #返回dones是已经完成的任务,pending是未完成的任务,都是集合类型

task1=asyncio.ensure_future(coroutine1)

task2=asyncio.ensure_future(coroutine2)tasks=asyncio.wait([task1,task2])

loop.run_until_complete(tasks)

Usage:

done, pending = yield from asyncio.wait(fs)

wait是接收一个列表,而后gather是接收一堆任务数据。

两者的返回值也是不同的

协程嵌套,将多个协程封装到一个主协程中

ContractedBlock.gif

ExpandedBlockStart.gif

import asyncio,aiohttpasyncdef fetch_async(url):

print(url)async with aiohttp.ClientSession() assession:async with session.get(url) asresp:

print(resp.status)

print(awaitresp.text())

tasks= [fetch_async('http://www.baidu.com/'), fetch_async('http://www.cnblogs.com/ssyfj/')]

event_loop=asyncio.get_event_loop()

results= event_loop.run_until_complete(asyncio.gather(*tasks))

event_loop.close()

关于aiohttp模块的协程嵌套,嵌套更加明显

import asyncio,timeasyncdef func1(num):

print(num,'before---func1----')awaitasyncio.sleep(num)return "recv num %s"%numasyncdef main():

coroutine1= func1(5)

coroutine2= func1(3)

coroutine3= func1(4)

tasks=[

asyncio.ensure_future(coroutine1),

asyncio.ensure_future(coroutine2),

asyncio.ensure_future(coroutine3),

]

dones, pendings= await asyncio.wait(tasks)for task indones: #对已完成的任务集合进行操作

print("Task ret:",task.result())if __name__ == "__main__":

begin=time.time()

loop=asyncio.get_event_loop()

loop.run_until_complete(main())

loop.close()

end=time.time()

print(end-begin)

5 before---func1----

3 before---func1----

4 before---func1----Task ret: recv num4Task ret: recv num5Task ret: recv num3

5.000285863876343

也可以直接使用gather直接获取值

results = await asyncio.gather(*tasks)for result inresults:

print("Task ret:",result)

我们也可以不在main中处理结果,而是返回到主调用方进行处理

asyncdef main():

coroutine1= func1(5)

coroutine2= func1(3)

coroutine3= func1(4)

tasks=[

asyncio.ensure_future(coroutine1),

asyncio.ensure_future(coroutine2),

asyncio.ensure_future(coroutine3),

]return await asyncio.gather(*tasks)if __name__ == "__main__":

begin=time.time()

loop=asyncio.get_event_loop()

results= loop.run_until_complete(main())

for result in results:

print("Task ret: ",result)

loop.close()

end=time.time()

print(end-begin)

或者使用wait挂起

return awaitasyncio.wait(tasks)

----------------------------------------------------

dones,pendings=loop.run_until_complete(main())for task indones:

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

或者使用asyncio中的as_completed方法

Return an iterator whose values are coroutines.  #返回一个可迭代的协程函数值

When waitingfor the yielded coroutines you'll get the results (or

exceptions!) of the original Futures (or coroutines), inthe orderin which and as soon as they complete.

import asyncio,timeasyncdef func1(num):

print(num,'before---func1----')awaitasyncio.sleep(num)return "recv num %s"%numasyncdef main():

coroutine1= func1(5)

coroutine2= func1(3)

coroutine3= func1(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: ",result)if __name__ == "__main__":

begin=time.time()

loop=asyncio.get_event_loop()

loop.run_until_complete(main())

loop.close()

end=time.time()

print(end-begin)

协程停止

future对象有几个状态:

Pending

Running

Done

Cacelled

创建future的时候,task为pending,

事件循环调用执行的时候当然就是running,

调用完毕自然就是done,

如果需要停止事件循环,就需要先把task取消。

可以使用asyncio.Task获取事件循环的task

import asyncio,timeasyncdef func1(num):

print(num,'before---func1----')awaitasyncio.sleep(num)return "recv num %s"%numif __name__ == "__main__":

begin=time.time()

coroutine1= func1(5)

coroutine2= func1(3)

coroutine3= func1(4)

tasks=[

asyncio.ensure_future(coroutine1),

asyncio.ensure_future(coroutine2),

asyncio.ensure_future(coroutine3),

]

loop=asyncio.get_event_loop()try:

loop.run_until_complete(asyncio.wait(tasks))

except KeyboardInterruptase:

print(asyncio.Task.all_tasks())for task inasyncio.Task.all_tasks():  #获取所有任务

print(task.cancel())  #单个任务取消

loop.stop()    #需要先stop循环

loop.run_forever()  #需要在开启事件循环finally:

loop.close()  #统一关闭

end=time.time()

print(end-begin)

5 before---func1----

3 before---func1----

4 before---func1----{ wait_for= cb=[_wait.

als>._on_completion() at C:\Users\Administrator\AppData\Local\Programs\Python\Python35\lib\asyncio\tasks.py:428]>,

.py:361> wait_for=>, wait

_for= cb=[_wait.._on_completion() at C:\Users\Administrator\AppData\Loca

l\Programs\Python\Python35\lib\asyncio\tasks.py:428]>, wait_f

or= cb=[_wait.._on_completion() at C:\Users\Administrator\AppData\Local\

Programs\Python\Python35\lib\asyncio\tasks.py:428]>}  #未处理,刚刚挂起为pending状态

True #返回True,表示cancel取消成功

True

True

True3.014172315597534

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

Task was destroyed but it is pending!

因为cancel后task的状态依旧是pending

for task inasyncio.Task.all_tasks():

print(task)

print(task.cancel())

print(task)

wait_for= cb=[_wait.

ls>._on_completion() at C:\Users\Administrator\AppData\Local\Programs\Python\Python35\lib\asyncio\tasks.py:428]>True wait_for= cb=[_wait.._on_completion

() at C:\Users\Administrator\AppData\Local\Programs\Python\Python35\lib\asyncio\tasks.py:428]>

或者使用协程嵌套,main协程相当于最外层task,处理main函数即可

import asyncio,timeasyncdef func1(num):

print(num,'before---func1----')awaitasyncio.sleep(num)return "recv num %s"%numasyncdef main():

coroutine1= func1(5)

coroutine2= func1(3)

coroutine3= func1(4)

tasks=[

asyncio.ensure_future(coroutine1),

asyncio.ensure_future(coroutine2),

asyncio.ensure_future(coroutine3),

]

dones,pendings=awaitasyncio.wait(tasks)for task indones:

print("Task ret:",task.result())if __name__ == "__main__":

begin=time.time()

loop=asyncio.get_event_loop()

task=asyncio.ensure_future(main())try:

loop.run_until_complete(task)

except KeyboardInterruptase:

print(asyncio.gather(*asyncio.Task.all_tasks()).cancel())  #我们只是把上面的单个写成了所有任务集合取消,和协程嵌套关系不大。上面也可以这样写。不过协程嵌套可以简化代码

loop.stop()

loop.run_forever()finally:

loop.close()

end=time.time()

print(end-begin)

5 before---func1----

3 before---func1----

4 before---func1----

True3.008172035217285

感觉有点多余...

import asyncio,timeasyncdef func1(num):

print(num,'before---func1----')awaitasyncio.sleep(num)return "recv num %s"%numif __name__ == "__main__":

begin=time.time()

coroutine1= func1(5)

coroutine2= func1(3)

coroutine3= func1(4)

tasks=[

asyncio.ensure_future(coroutine1),

asyncio.ensure_future(coroutine2),

asyncio.ensure_future(coroutine3),

]

loop=asyncio.get_event_loop()try:

loop.run_until_complete(asyncio.wait(tasks))

except KeyboardInterruptase:

print(asyncio.gather(*tasks).cancel())

loop.stop()

loop.run_forever()finally:

loop.close()

end=time.time()

print(end-begin)

5 before---func1----

3 before---func1----

4 before---func1----True3.008171796798706

上面讨论的都是在同一线程下的事件循环,下面来谈谈不同线程的事件循环

在当前线程中创建一个事件循环(不启用,单纯获取标识),开启一个新的线程,在新的线程中启动事件循环。在当前线程依据事件循环标识,可以向事件中添加协程对象。当前线程不会由于事件循环而阻塞了。

上面在一个线程中执行的事件循环,只有我们主动关闭事件close,事件循环才会结束,会阻塞。

同一线程:

import asyncio,timeasyncdef func1(num):

print(num,'before---func1----')awaitasyncio.sleep(num)return "recv num %s"%numif __name__ == "__main__":

begin=time.time()

coroutine1= func1(5)

coroutine2= func1(3)

coroutine3= func1(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))

loop.run_forever()

end=time.time()

print(end-begin)

1309518-20180624160545351-549432134.png

不同线程事件循环(不涉及协程):

import asyncio,time,threading

def func1(num):

print(num,'before---func1----')

time.sleep(num)return "recv num %s"%num

def start_loop(loop):

asyncio.set_event_loop(loop)

loop.run_forever()if __name__ == "__main__":

begin=time.time()

new_loop=asyncio.new_event_loop() #在当前线程下创建时间循环,(未启用)

t= threading.Thread(target=start_loop,args=(new_loop,)) #开启新的线程去启动事件循环

t.start()

new_loop.call_soon_threadsafe(func1,3)

new_loop.call_soon_threadsafe(func1,2)

new_loop.call_soon_threadsafe(func1,6)

end=time.time()

print(end-begin) #当前线程未阻塞,耗时0.02800154685974121

3 before---func1----

0.02800154685974121

2 before---func1----

6 before---func1----

新线程协程:

import asyncio,time,threadingasyncdef func1(num):

print(num,'before---func1----')awaitasyncio.sleep(num)return "recv num %s"%num

def start_loop(loop):

asyncio.set_event_loop(loop)

loop.run_forever()if __name__ == "__main__":

begin=time.time()

coroutine1= func1(5)

coroutine2= func1(3)

coroutine3= func1(4)

new_loop=asyncio.new_event_loop() #在当前线程下创建时间循环,(未启用)

t= threading.Thread(target=start_loop,args=(new_loop,)) #开启新的线程去启动事件循环

t.start()

asyncio.run_coroutine_threadsafe(coroutine1,new_loop)  #传参必须是协程对象

asyncio.run_coroutine_threadsafe(coroutine2,new_loop)

asyncio.run_coroutine_threadsafe(coroutine3,new_loop)

end=time.time()

print(end-begin) #当前线程未阻塞,耗时0.010000467300415039

5 before---func1----

3 before---func1----

4 before---func1----

0.010000467300415039

主线程通过run_coroutine_threadsafe新注册协程对象。这样就能在子线程中进行事件循环的并发操作,同时主线程又不会被block。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值