一:使用 asyncio处理并发
介绍 asyncio 包,这个包使用事件循环驱动的协程实现并发。这是 Python 中最大也是最具雄心壮志的库之一。
二:示例
1)单任务协程处理和普通任务比较
#普通任务示例 # _*_ coding:utf-8 _*_ __author__ = "lixiang" import asyncio import time def hello(): count=0 while count<10: print("before",count) try: time.sleep(0.1) count+=1 print("after",count) except Exception as e: print(e) return count if __name__=="__main__": start_time=time.time() hello() end_time=time.time() print("cost time",end_time-start_time)
#单任务协程示例 # _*_ coding:utf-8 _*_ __author__ = "lixiang" import asyncio import time @asyncio.coroutine# 表示要协程处理 def hello(): count=0 while count<10: print("before",count) try: #使用 yield from asyncio.sleep(1) 代替 time.sleep(1), yield from asyncio.sleep(0.1) count+=1 print("after",count) except Exception as e: print(e) return count if __name__=="__main__": start_time=time.time() loop=asyncio.get_event_loop() loop.run_until_complete(hello()) loop.close() end_time=time.time() print("cost time",end_time-start_time)
两种方式处理结果处理时间都是1秒,此时协程并不能体现并发的优势
# _*_ coding:utf-8 _*_ __author__ = "lixiang" import asyncio import time @asyncio.coroutine# 要协程处理 def hello(): count=0 while count<10: print("before",count) try: #使用 yield from asyncio.sleep(0.1) 代替 time.sleep(0.1),这样的休眠不会阻塞事件循环 yield from asyncio.sleep(0.1) count+=1 print("after",count) except Exception as e: print(e) return count @asyncio.coroutine def hello_welcome(): result=yield from hello() print(result) if __name__=="__main__": start_time=time.time() loop=asyncio.get_event_loop() loop.run_until_complete(hello_welcome()) loop.close() end_time=time.time() print("cost time",end_time-start_time)
此时也没有显示协程优势
2)两个任务协程比较
上面示例,如果hello运行需要1秒,别外一个任务运行也需要1秒。
# _*_ coding:utf-8 _*_ __author__ = "lixiang" import asyncio import time @asyncio.coroutine# 要协程处理 def hello(): count=0 while count<10: print("before",count) try: #使用 yield from asyncio.sleep(0.1) 代替 time.sleep(0.1),这样的休眠不会阻塞事件循环 yield from asyncio.sleep(0.1) count+=1 print("after",count) except Exception as e: print(e) return count @asyncio.coroutine def show_hello(): yield from asyncio.sleep(1) return 666 @asyncio.coroutine def hello_welcome(): """ 安排协程hello的运行时间,这个运行时候根据show_hello来定。 如果show_hello安排时间为0.5秒,hello时间为0.1秒,10次循环不能正常结束, 提示Task was destroyed but it is pending! 如果show_hello安排时间为1秒,hello时间为0.1秒,10次循环能正常结束, 任务提示:Task finished """ re1=asyncio.async(hello()) result=yield from show_hello() print(re1) print("result",result) if __name__=="__main__": start_time=time.time() loop=asyncio.get_event_loop() loop.run_until_complete(hello_welcome()) loop.close() end_time=time.time() print("cost time",end_time-start_time) #测试结果显示只需要1秒,并发优势显示出来。 这里我们对hello进行协程运行时间安排。 安排协程hello的运行时间,这个运行时间根据show_hello来定。 如果show_hello安排时间为0.5秒,hello时间为0.1秒,10次循环不能正常结束, 提示Task was destroyed but it is pending! 如果show_hello安排时间为0.9秒,hello时间为0.1秒,10次循环能正常结束, 任务提示:Task finished
#多任务 # _*_ coding:utf-8 _*_ __author__ = "lixiang" import asyncio import time @asyncio.coroutine# 要协程处理 def hello(): count=0 while count<10: print("before",count) try: #使用 yield from asyncio.sleep(.1) 代替 time.sleep(.1),这样的休眠不会阻塞事件循环 yield from asyncio.sleep(0.1) count+=1 print("after",count) except Exception as e: print(e) return count @asyncio.coroutine def show_hello(): yield from asyncio.sleep(1) return 666 @asyncio.coroutine def show_hello2(): yield from asyncio.sleep(1) return 777 @asyncio.coroutine def hello_welcome(): """ 安排协程hello的运行时间,这个运行时候根据show_hello来定。 如果show_hello安排时间为0.5秒,hello时间为0.1秒,10次循环不能正常结束, 提示Task was destroyed but it is pending! 如果show_hello安排时间为0.9秒,hello时间为0.1秒,10次循环能正常结束, 任务提示:Task finished """ asyncio.async(show_hello2()) asyncio.async(hello()) result=yield from show_hello() print("result",result) if __name__=="__main__": start_time=time.time() loop=asyncio.get_event_loop() loop.run_until_complete(hello_welcome()) loop.close() end_time=time.time() print("cost time",end_time-start_time)
@asyncio.coroutine# 要协程处理 def hello(): count=0 while count<10: print("before",count) try: #使用 yield from asyncio.sleep(.1) 代替 time.sleep(.1),这样的休眠不会阻塞事件循环 yield from asyncio.sleep(0.1) count+=1 print("after",count) yield from asyncio.sleep(0.1) except Exception as e: print(e)
上面为了执行这些操作,必须排定协程的运行时间,然后使用 asyncio.Task 对象包装协
程。对协程来说,获取 Task 对象有两种主要方式。
asyncio.async(coro_or_future, *, loop=None)
create_
上面如果都是通过时间控制协程运行,那如果控制协程的时间怎么设定比较好了,
时间短了其他协程任务不能运行结束Task was destroyed but it is pending,时间设置长又会耗时长了。
可以通过asyncio.wait解决
asyncio.wait(...) 协程的参数是一个由期物或协程构成的可迭代对象; wait 会分别
把各个协程包装进一个 Task 对象。最终的结果是, wait 处理的所有对象都通过某种方
式变成 Future 类的实例。 wait 是协程函数,因此返回的是一个协程或生成器对
象; wait_coro 变量中存储的正是这种对象。为了驱动协程,我们把协程传给
loop.run_until_complete(...) 方法。虽然函数的名称是 wait,但它不是阻塞型函数。
wait 是一个协程,等传给它的所有协程运行完毕后结束(这是 wait 函数的默认行为;参见这个示例后面的说明)
loop.run_until_complete 方法的参数是一个期物或协程。如果是协
程, run_until_complete 方法与 wait 函数一样,把协程包装进一个 Task 对象中。协
程、期物和任务都能由 yield from 驱动,这正是 run_until_complete 方法对 wait
函数返回的 wait_coro 对象所做的事。 wait_coro 运行结束后返回一个元组,第一个元
素是一系列结束的期物,第二个元素是一系列未结束的期物
# _*_ coding:utf-8 _*_ __author__ = "lixiang" import asyncio import time @asyncio.coroutine# 要协程处理 def hello(): count=0 while count<10: print("before",count) try: #使用 yield from asyncio.sleep(.1) 代替 time.sleep(.1),这样的休眠不会阻塞事件循环 yield from asyncio.sleep(0.1) count+=1 print("after",count) yield from asyncio.sleep(0.1) except Exception as e: print(e) return count @asyncio.coroutine def show_hello(): yield from asyncio.sleep(1.2) return 666 @asyncio.coroutine def show_hello2(): yield from asyncio.sleep(1.2) return 777 # @asyncio.coroutine # def hello_welcome(): # """ # 安排协程hello的运行时间,这个运行时候根据show_hello来定。 # 如果show_hello安排时间为0.5秒,hello时间为0.1秒,10次循环不能正常结束, # 提示Task was destroyed but it is pending! # 如果show_hello安排时间为0.9秒,hello时间为0.1秒,10次循环能正常结束, # 任务提示:Task finished # """ # asyncio.async(show_hello2()) # asyncio.async(hello()) # result=yield from show_hello() # # print("result",result) if __name__=="__main__": start_time=time.time() loop=asyncio.get_event_loop() wait_coro=asyncio.wait([hello(),show_hello(),show_hello2()]) loop.run_until_complete(wait_coro) wait_coro.close() loop.close() end_time=time.time() print("cost time",end_time-start_time)
#如果要实时显示那个协程已完成了
我把一个协程列表传给 asyncio.wait 函数,经由
loop.run_until_complete 方法驱动,全部协程运行完毕后,这个函数会返回所有下载
结果。可是,为了更新进度条,各个协程运行结束后就要立即获取结果。在线程池版示例
中(见示例 17-14),为了集成进度条,我们使用的是 as_completed 生成器函数;幸
好, asyncio 包提供了这个生成器函数的相应版本
# _*_ coding:utf-8 _*_ __author__ = "lixiang" import asyncio import time @asyncio.coroutine# 要协程处理 def hello(): count=0 while count<10: print("before",count) try: #使用 yield from asyncio.sleep(.1) 代替 time.sleep(.1),这样的休眠不会阻塞事件循环 yield from asyncio.sleep(0.1) count+=1 print("after",count) yield from asyncio.sleep(0.1) except Exception as e: print(e) return count @asyncio.coroutine def show_hello(): yield from asyncio.sleep(1.2) return 666 @asyncio.coroutine def show_hello2(): yield from asyncio.sleep(1.3) return 777 @asyncio.coroutine def hello_welcome(): count=0 coro=asyncio.as_completed([hello(),show_hello(),show_hello2()]) for future in coro: res=yield from future print("11111",res) count+=1 print(count)#可以在这里实现进度条功能 return count if __name__=="__main__": start_time=time.time() loop=asyncio.get_event_loop() counts=loop.run_until_complete(hello_welcome()) print(counts) loop.close() end_time=time.time() print("cost time",end_time-start_time)