协程asyncio_Python协程asyncio

Python 3.7 提供了新的基于 asyncio 和 async / await 的方法。

  • 说并发,你肯定想到了多线程 / 多进程模型,没错,多线程 / 多进程,正是解决并发问题的经典模型之一。
  • 但协程是实现并发编程的一种方式。
  • 事件循环启动一个统一的调度器,让调度器来决定一个时刻去运行哪个任务。

1、一个爬虫例子

import time

def crawl_page(url):
    print('crawling {}'.format(url))
    sleep_time = int(url.split('_')[-1])
    time.sleep(sleep_time)
    print('OK {}'.format(url))

def main(urls):
    for url in urls:
        crawl_page(url)

main(['url_1', 'url_2', 'url_3', 'url_4'])

但你仔细一算,它也占用了不少时间,五个页面分别用了 1 秒到 4 秒的时间,加起来一共用了 10 秒。

2、我们就来看看使用协程怎么写。

import asyncio

async 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)

asyncio.run(main(['url_1', 'url_2', 'url_3', 'url_4']))
  • await 是同步调用,因此, crawl_page(url) 在当前的调用结束之前,是不会触发下一次调用的。于是,这个代码效果就和上面完全一样了,相当于我们用异步接口写了个同步代码。
  • async 修饰词声明异步函数,于是,这里的 crawl_page 和 main 都变成了异步函数。
  • 而调用异步函数,我们便可得到一个协程对象(coroutine object)。

3、再来说说协程的执行

执行协程有多种方法,这里我介绍一下常用的三种。

  • 我们可以通过 await 来调用。await 执行的效果,和 Python 正常执行是一样的,也就是说程序会阻塞在这里,进入被调用的协程函数,执行完毕返回后再继续,而这也是 await 的字面意思。(同步调用)
  • 我们可以通过 asyncio.create_task() 来创建任务
  • 我们需要 asyncio.run 来触发运行。

4、协程中的一个重要概念,任务(Task)

import asyncio

async 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

asyncio.run(main(['url_1', 'url_2', 'url_3', 'url_4']))

########## 输出 ##########

Wall time: 3.99 s
  • 我们有了协程对象后,便可以通过 asyncio.create_task 来创建任务。
  • 任务创建后很快就会被调度执行,这样,我们的代码也不会阻塞在任务这里。
  • 所以,我们要等所有任务都结束才行,用for task in tasks: await task 即可。

5、解密协程运行时

import asyncio

async def worker_1():
    print('worker_1 start')
    await asyncio.sleep(1)
    print('worker_1 done')

async def worker_2():
    print('worker_2 start')
    await asyncio.sleep(2)
    print('worker_2 done')

async def main():
    print('before await')
    await worker_1()
    print('awaited worker_1')
    await worker_2()
    print('awaited worker_2')

asyncio.run(main())

########## 输出 ##########

before await
worker_1 start
worker_1 done
awaited worker_1
worker_2 start
worker_2 done
awaited worker_2

Wall time: 3 s

第二个代码

import asyncio
# asyncio.create_task() 创建任务
# asyncio.sleep()在这里休息若干秒
# asyncio.run()主程序的入口函数
# async修饰词声明异步函数
# await进入被调用的协程函数,执行完毕返回后再继续

# 4 worker_1 开始运行,运行 print 输出'worker_1 start'
async def worker_1():
    print('worker_1 start')
# 5 然后运行到 await asyncio.sleep(1), 从当前任务切出,事件调度器开始调度 worker_2
    await asyncio.sleep(1)
# 8 一秒钟后,worker_1 的 sleep 完成,
# 事件调度器将控制权重新传给 task_1,输出 'worker_1 done',task_1 完成任务,从事件循环中退出;
    print('worker_1 done')


# 6 orker_2 开始运行,运行 print 输出 'worker_2 start'
async def worker_2():
    print('worker_2 start')
# 7 然后运行 await asyncio.sleep(2) 从当前任务切出
    await asyncio.sleep(2)
# 10 两秒钟后,worker_2 的 sleep 完成,事件调度器将控制权重新传给 task_2,
# 输出 'worker_2 done',task_2 完成任务,从事件循环中退出;
    print('worker_2 done')


async def main():
# 2 task1 和 task2 任务被创建
    task1 = asyncio.create_task(worker_1())
    task2 = asyncio.create_task(worker_2())
    print('before await')
# 3 选择从当前的主任务中切出,事件调度器开始调度 worker_1
    await task1
    print('awaited worker_1')
# 9 await task1 完成,事件调度器将控制器传给主任务,输出 'awaited worker_1',
# 然后在 await task2 处继续等待;
    await task2
    print('awaited worker_2')
# 11 主任务输出 'awaited worker_2',协程全任务结束,事件循环结束。


# 1事件循环开启
asyncio.run(main())
  1. asyncio.run(main()),程序进入 main() 函数,事件循环开启;
  2. task1 和 task2 任务被创建,并进入事件循环等待运行;
  3. 运行到 print,输出 'before await';await task1 执行,用户选择从当前的主任务中切出,事件调度器开始调度 worker_1;
  4. worker_1 开始运行,运行 print 输出'worker_1 start',然后运行到 await asyncio.sleep(1), 从当前任务切出,事件调度器开始调度 worker_2;
  5. worker_2 开始运行,运行 print 输出 'worker_2 start',然后运行 await asyncio.sleep(2) 从当前任务切出;
  6. 以上所有事件的运行时间,都应该在 1ms 到 10ms 之间,甚至可能更短,事件调度器从这个时候开始暂停调度;
  7. 一秒钟后,worker_1 的 sleep 完成,事件调度器将控制权重新传给 task_1,输出 'worker_1 done',task_1 完成任务,从事件循环中退出;
  8. await task1 完成,事件调度器将控制器传给主任务,输出 'awaited worker_1',·然后在 await task2 处继续等待;
  9. 两秒钟后,worker_2 的 sleep 完成,事件调度器将控制权重新传给 task_2,输出 'worker_2 done',task_2 完成任务,从事件循环中退出;
  10. 主任务输出 'awaited worker_2',协程全任务结束,事件循环结束。

协成里面重要的是一个关键字await的理解,async表示其修饰的是协程任务即task,await表示的是当线程执行到这一句,此时该task在此处挂起,然后调度器去执行其他的task,当这个挂起的部分处理完,会调用回掉函数告诉调度器我已经执行完了,那么调度器就返回来处理这个task的余下语句。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值