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)
并发:使用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是接收一堆任务数据。
两者的返回值也是不同的
协程嵌套,将多个协程封装到一个主协程中
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)
不同线程事件循环(不涉及协程):
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。