Python协程-asyncio、async/await  
        
      
     
    
   
   
看到吐血 (´ཀ`」 ∠) 
 
协程(Coroutine)本质上是一个函数,特点是在代码块中可以将执行权交给其他协程 
众所周知,子程序(函数)都是层级调用的,如果在A中调用了B,那么B执行完毕返回后A才能执行完毕。协程与子程序有点类似,但是它在执行过程中可以中断,转而执行其他的协程,在适当的时候再回来继续执行。  
协程与多线程相比的最大优势在于:协程是一个线程中执行,没有线程切换的开销;协程由用户决定在哪里交出控制权 
这里用到的是asyncio库(Python 3.7),这个库包含了大部分实现协程的魔法工具
使用 async 修饰词声明异步函数 
使用 await 语句执行可等待对象(Coroutine、Task、Future) 
使用 asyncio.create_task 创建任务,将异步函数(协程)作为参数传入,等待event loop 执行 
使用 asyncio.run 函数运行协程程序,协程函数作为参数传入 
 
 
 
 
解析协程运行时 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import  asyncioimport  timeasync  def  a ():    print ("欢迎使用 a !" )     await  asyncio.sleep(1 )     print ("欢迎回到 a !" ) async  def  b ():    print ("欢迎来到 b !" )     await  asyncio.sleep(2 )     print ("欢迎回到 b !" ) async  def  main ():    task1 = asyncio.create_task(a())     task2 = asyncio.create_task(b())     print ("准备开始" )     await  task1     print ("task1 结束" )     await  task2     print ("task2 结束" ) if  __name__ == "__main__" :    start = time.perf_counter()     asyncio.run(main())     print ('花费 {} s' .format (time.perf_counter() - start)) 
 
解释:
1、asyncio.run(main()),程序进入main()函数,开启事件循环 
2、创建任务task1、task2并进入事件循环等待运行 
3、输出准备开始 
4、执行await task1,用户选择从当前主任务中切出,事件调度器开始调度 a  
5、a 开始运行,输出欢迎使用a!,运行到await asyncio.sleep(1),从当前任务切出,事件调度器开始调度 b  
6、b 开始运行,输出欢迎来到b!,运行到await asyncio.sleep(2),从当前任务切出  
7、以上事件运行时间非常短(毫秒),事件调度器开始暂停调度  
8、一秒钟后,a的sleep完成,事件调度器将控制权重新交给a ,输出欢迎回到a!,task1完成任务,退出事件循环 
9、await task1完成,事件调度器将控制权还给主任务,输出task1结束,然后在await task2处继续等待 
10、两秒钟后,b的sleep完成,事件调度器将控制权重新传给 b ,输出欢迎回到 b!,task2完成任务,从事件循环中退出 
11、事件调度器将控制权交还给主任务,主任务输出task2结束,至此协程任务全部结束,事件循环结束。 
 
 
 
上面的代码也可以这样写,将15到21行换成一行await asyncio.gather(a(), b())也能实现类似的效果 ,await asyncio.gather 会并发运行传入的可等待对象(Coroutine、Task、Future)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import  asyncioimport  timeasync  def  a ():    print ("欢迎使用 a !" )     await  asyncio.sleep(1 )     print ("欢迎回到 a !" ) async  def  b ():    print ("欢迎来到 b !" )     await  asyncio.sleep(2 )     print ("欢迎回到 b !" ) async  def  main ():    await  asyncio.gather(a(), b()) if  __name__ == "__main__" :    start = time.perf_counter()     asyncio.run(main())     print ('花费 {} s' .format (time.perf_counter() - start)) 
 
异步接口同步实现 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 """ - 简单爬虫模拟 - 这里用异步接口写了个同步代码 """ import  asyncioimport  timeasync  def  crawl_page (url ):    print ('crawling {}' .format (url))     sleep_time = int (url.split('_' )[-1 ])     await  asyncio.sleep(sleep_time)       print ('OK {}' .format (url)) async  def  main (urls ):    for  url in  urls:         await  crawl_page(url)   start = time.perf_counter() asyncio.run(main(['url_1' , 'url_2' ]))  print ("Cost {} s" .format (time.perf_counter() - start))
 
使用 Task 实现异步 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import  asyncioimport  timeasync  def  crawl_page (url ):    print ('crawling {}' .format (url))     sleep_time = int (url.split('_' )[-1 ])     await  asyncio.sleep(sleep_time)     print ('OK {}' .format (url)) async  def  main (urls ):    tasks = [asyncio.create_task(crawl_page(url)) for  url in  urls]     for  task in  tasks:         await  task           start = time.perf_counter() asyncio.run(main(['url_1' , 'url_2' ])) print ("Cost {} s" .format (time.perf_counter() - start))
 
生产者消费者模型的协程版本 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 import  asyncioimport  randomimport  timeasync  def  consumer (queue, id  ):    """消费者"""      while  True :         val = await  queue.get()         print ('{} get a val : {}' .format (id , val))         await  asyncio.sleep(1 ) async  def  producer (queue, id  ):    """生产者"""      for  _ in  range (5 ):         val = random.randint(1 , 10 )         await  queue.put(val)         print ('{} put a val: {}' .format (id , val))         await  asyncio.sleep(1 ) async  def  main ():    queue = asyncio.Queue()     consumer_1 = asyncio.create_task(consumer(queue, 'consumer_1' ))     consumer_2 = asyncio.create_task(consumer(queue, 'consumer_2' ))     producer_1 = asyncio.create_task(producer(queue, 'producer_1' ))     producer_2 = asyncio.create_task(producer(queue, 'producer_2' ))     await  asyncio.sleep(10 )          consumer_1.cancel()     consumer_2.cancel()          await  asyncio.gather(consumer_1, consumer_2, producer_1, producer_2, return_exceptions=True ) if  __name__ == "__main__" :    start = time.perf_counter()     asyncio.run(main())     print ("Cost {} s" .format (time.perf_counter() - start)) 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 # 输出结果 producer_1 put a val: 7 producer_2 put a val: 4 consumer_1 get a val : 7 consumer_2 get a val : 4 producer_1 put a val: 6 producer_2 put a val: 1 consumer_2 get a val : 6 consumer_1 get a val : 1 producer_1 put a val: 8 producer_2 put a val: 1 consumer_1 get a val : 8 consumer_2 get a val : 1 producer_1 put a val: 6 producer_2 put a val: 4 consumer_2 get a val : 6 consumer_1 get a val : 4 producer_1 put a val: 7 producer_2 put a val: 8 consumer_1 get a val : 7 consumer_2 get a val : 8 Cost 10.0093015 s 
 
拓展阅读:Python的生产者消费者模型,看这篇就够了 
参考