python中asyncio的使用实例

本文介绍Python3中使用asyncio模块、await和async关键字进行异步I/O编程的方法,通过示例展示了如何并发抓取多个网站的数据。

Python 3通过asyncio模块以及awaitasync关键字提供了对异步I/O的支持。

步编程通常通过多任务协作处理的方式来实现,由于执行时间和顺序的不确定,因此需要通过钩子函数(回调函数)或者Future对象来获取任务执行的结果。

import asyncio

async def fetch(host):
    # 从指定站点抓取信息
    print(f'开始抓取{host}')
    # 和服务器建立连接
    reader, writer = await asyncio.open_connection(host, 80)
    # 构建请求行和请求头
    writer.write(b'GET / HTTP/1.1\r\n')
    writer.write(f'Host:{host}\r\n'.encode())
    writer.write(b'\r\n')
    # 清空缓存区
    await writer.drain()
    # 接收服务器的响应
    line = await reader.readline()
    while line != b'\r\n':
        print(line.decode().rsplit())
        line = await reader.readline()
    print('\n')
    writer.close()

def main():
    urls = ('www.baidu.com', 'www.youkuaiyun.com', 'www.163.com')
    # 获取系统默认事件循环
    loop = asyncio.get_event_loop()
    # 生成式语法,构造一个包含多个协议对象的列表
    tasks = [fetch(url) for url in urls]
    # wait: 将协程列表包装成Task(Future)并等待执行完成
    # run_until_complete: 运行任务,直到Future执行完成并返回结果
    loop.run_until_complete(asyncio.wait(tasks))
    loop.close()

if __name__ == '__main__':
    main()

执行结果:

开始抓取www.163.com
开始抓取www.baidu.com
开始抓取www.youkuaiyun.com
['HTTP/1.1', '200', 'OK']
['Date:', 'Tue,', '31', 'Mar', '2020', '15:08:46', 'GMT']
['Content-Type:', 'text/html;', 'charset=GBK']
['Transfer-Encoding:', 'chunked']
['Connection:', 'keep-alive']
['Expires:', 'Tue,', '31', 'Mar', '2020', '15:09:59', 'GMT']
['Server:', 'nginx']
['Cache-Control:', 'no-cache,no-store,private']
['Age:', '7']
['Vary:', 'Accept-Encoding']
['X-Ser:', 'BC80_dx-lt-yd-jiangsu-taizhou-4-cache-4,', 'BC84_dx-lt-yd-jiangsu-taizhou-4-cache-4,', 'BC233_ck-hubei-wuhan-2-cache-2,', 'BC196_ck-hubei-wuhan-2-cache-1']
['cdn-user-ip:', '49.211.105.136']
['cdn-ip:', '124.203.225.195']
['X-Cache-Remote:', 'HIT']
['cdn-source:', 'baishan']


['HTTP/1.1', '200', 'OK']
['Accept-Ranges:', 'bytes']
['Cache-Control:', 'no-cache']
['Connection:', 'keep-alive']
['Content-Length:', '14615']
['Content-Type:', 'text/html']
['Date:', 'Tue,', '31', 'Mar', '2020', '15:08:46', 'GMT']
['P3p:', 'CP="', 'OTI', 'DSP', 'COR', 'IVA', 'OUR', 'IND', 'COM', '"']
['P3p:', 'CP="', 'OTI', 'DSP', 'COR', 'IVA', 'OUR', 'IND', 'COM', '"']
['Pragma:', 'no-cache']
['Server:', 'BWS/1.1']
['Set-Cookie:', 'BAIDUID=9086941EE74E01736F33F9B8CF3844A2:FG=1;', 'expires=Thu,', '31-Dec-37', '23:55:55', 'GMT;', 'max-age=2147483647;', 'path=/;', 'domain=.baidu.com']
['Set-Cookie:', 'BIDUPSID=9086941EE74E01736F33F9B8CF3844A2;', 'expires=Thu,', '31-Dec-37', '23:55:55', 'GMT;', 'max-age=2147483647;', 'path=/;', 'domain=.baidu.com']
['Set-Cookie:', 'PSTM=1585667326;', 'expires=Thu,', '31-Dec-37', '23:55:55', 'GMT;', 'max-age=2147483647;', 'path=/;', 'domain=.baidu.com']
['Set-Cookie:', 'BAIDUID=9086941EE74E01739D5D416533CDA255:FG=1;', 'max-age=31536000;', 'expires=Wed,', '31-Mar-21', '15:08:46', 'GMT;', 'domain=.baidu.com;', 'path=/;', 'version=1;', 'comment=bd']
['Traceid:', '1585667326274850689010390091619431081419']
['Vary:', 'Accept-Encoding']
['X-Ua-Compatible:', 'IE=Edge,chrome=1']


['HTTP/1.1', '301', 'Moved', 'Permanently']
['Server:', 'openresty']
['Date:', 'Tue,', '31', 'Mar', '2020', '15:08:46', 'GMT']
['Content-Type:', 'text/html']
['Content-Length:', '182']
['Connection:', 'keep-alive']
['Keep-Alive:', 'timeout=20']
['Location:', 'https://www.youkuaiyun.com/']

Process finished with exit code 0
 

<think> 我们被要求提供一个简单的Python asyncio运用实例。根据引用[3]中的例子,我们可以写一个并发访问多个网址的简单程序。同时,引用[1]中展示了如何使用事件循环和任务的基本结构。我们将结合这些,给出一个简单清晰的实例。 目标:使用asyncio并发执行多个异步任务(模拟I/O操作,如网络请求)。 步骤: 1. 定义一个异步函数(协程),模拟一个I/O操作(这里使用asyncio.sleep来模拟耗时)。 2. 创建多个协程任务。 3. 使用事件循环运行这些任务。 注意:在较新的Python版本(3.7及以上)中,我们可以使用`asyncio.run()`来运行主协程,而不需要手动管理事件循环。 实例:模拟三个并发任务,每个任务等待不同的时间(1秒、2秒、3秒),并打印开始和结束信息。 代码: ```python import asyncio import time # 定义一个异步函数,模拟一个耗时的I/O操作 async def task(name, seconds): print(f"任务 {name} 开始,将等待 {seconds} 秒") await asyncio.sleep(seconds) # 模拟等待,此时事件循环可以运行其他任务 print(f"任务 {name} 结束") # 主协程 async def main(): # 创建三个任务(此时任务还未运行,只是创建) task1 = asyncio.create_task(task("A", 1)) task2 = asyncio.create_task(task("B", 2)) task3 = asyncio.create_task(task("C", 3)) # 等待所有任务完成(并发执行) await task1 await task2 await task3 # 运行主协程 if __name__ == "__main__": start_time = time.time() asyncio.run(main()) end_time = time.time() print(f"总耗时: {end_time - start_time:.2f} 秒") ``` 运行结果分析: 由于任务是并发执行的,所以总时间不会超过最长任务的时间(即3秒)。输出可能如下: ``` 任务 A 开始,将等待 1 秒 任务 B 开始,将等待 2 秒 任务 C 开始,将等待 3 秒 (等待1秒后) 任务 A 结束 (再等1秒后) 任务 B 结束 (再等1秒后) 任务 C 结束 总耗时: 3.00 秒 ``` 代码说明: - 使用`async def`定义协程函数`task`,其中`await asyncio.sleep(seconds)`表示挂起当前协程,让出控制权给事件循环,事件循环在此期间可以运行其他任务。 - 在`main`协程中,使用`asyncio.create_task()`将协程包装成任务并加入事件循环。此时任务开始运行(注意:任务在创建后会在事件循环中尽快运行,但具体执行顺序由事件循环调度)。 - 使用`await`等待每个任务完成。注意:等待的顺序不影响任务执行的并发性,因为任务已经在运行了。 - 使用`asyncio.run(main())`运行主协程(Python 3.7+)。 注意:这个例子中,我们使用了`asyncio.create_task`来创建任务并立即开始运行。在`main`函数中,我们创建了三个任务,然后分别等待它们。由于任务在创建时就已经开始运行,因此当我们等待第一个任务时,其他任务也在同时运行。 另外,我们记录了程序开始和结束的时间,以展示总耗时大约是3秒(即最长任务的时间),从而验证了并发执行。 这个例子简单展示了asyncio的基本用法:定义协程、创建任务、并发执行。 §§相关问题§§ 1. 如何取消正在运行的asyncio任务? 2. asyncio中如何处理异常? 3. 如何限制asyncio的并发任务数量? </think> ### 简单 Python asyncio 运用实例 下面是一个完整的 `asyncio` 运用实例,模拟并发下载多个网页(使用 `asyncio.sleep` 替代真实网络请求): ```python import asyncio import time # 异步任务:模拟网页下载 async def download_page(page_id, delay): start = time.time() print(f"📥 开始下载页面 {page_id} (预计耗时 {delay}秒)") # 使用 await 挂起等待(模拟网络请求) await asyncio.sleep(delay) end = time.time() print(f"✅ 页面 {page_id} 下载完成! 耗时: {end-start:.2f}秒") return f"页面{page_id}内容" # 主协程:批量执行异步任务 async def main(): # 创建任务列表(模拟5个网页下载) tasks = [ asyncio.create_task(download_page(1, 1.5)), asyncio.create_task(download_page(2, 0.8)), asyncio.create_task(download_page(3, 2.0)), asyncio.create_task(download_page(4, 1.0)), asyncio.create_task(download_page(5, 0.5)) ] # 并发执行所有任务并获取结果 results = await asyncio.gather(*tasks) # 打印所有结果 print("\n所有任务完成结果:") for i, result in enumerate(results, 1): print(f"页面{i}: {result}") # 启动事件循环 if __name__ == "__main__": start_time = time.time() asyncio.run(main()) print(f"\n总耗时: {time.time()-start_time:.2f}秒") ``` #### ▶️ 输出结果示例: ``` 📥 开始下载页面 1 (预计耗时 1.5秒) 📥 开始下载页面 2 (预计耗时 0.8秒) 📥 开始下载页面 3 (预计耗时 2.0秒) 📥 开始下载页面 4 (预计耗时 1.0秒) 📥 开始下载页面 5 (预计耗时 0.5秒) ✅ 页面 5 下载完成! 耗时: 0.50秒 ✅ 页面 2 下载完成! 耗时: 0.80秒 ✅ 页面 4 下载完成! 耗时: 1.00秒 ✅ 页面 1 下载完成! 耗时: 1.50秒 ✅ 页面 3 下载完成! 耗时: 2.00秒 所有任务完成结果: 页面1: 页面1内容 页面2: 页面2内容 页面3: 页面3内容 页面4: 页面4内容 页面5: 页面5内容 总耗时: 2.00秒 ``` #### ✨ 关键点解析: 1. **异步函数定义** 使用 `async def` 声明协程函数,内部通过 `await` 挂起阻塞操作: ```python async def download_page(page_id, delay): await asyncio.sleep(delay) # 模拟I/O等待 ``` 2. **任务并发执行** `asyncio.gather()` 并发启动所有任务[^3]: ```python results = await asyncio.gather(*tasks) ``` 3. **事件循环管理** 使用 `asyncio.run()` 简化事件循环管理(Python 3.7+)[^1]: ```python asyncio.run(main()) ``` 4. **执行效率** 尽管任务总耗时 $T_{total} = \sum delay_i = 5.8$秒,但异步执行只需约 $max(delay_i) = 2$秒,实现了并发加速。 #### 💡 拓展应用 将此实例扩展为实战项目: ```python # 替换为真实网络请求(需安装 aiohttp) import aiohttp async def real_download(url): async with aiohttp.ClientSession() as session: async with session.get(url) as response: return await response.text() ```
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值