同步代码跑得慢?是时候拥抱异步了
各位兄弟们好啊。
不知道你们有没有遇到过这种场景——要读取一堆文件,或者请求一堆API,同步代码跑起来慢得要死,电脑风扇呼呼转,结果一看进度条才10%。
别急,今天教你的异步编程,就是来解决这个痛点的。
先说说啥是异步,别整那些概念
大白话讲:同步就是你排队买东西,轮到你才能下一位;异步就是扫码点单,不用干等着,饭好了叫你。
举两个例子对比一下,你就懂了。
# 同步版本 - 读取3个文件
import time
defsync_read_files():
start = time.time()
result = []
for i in range(3):
with open(f'file_{i}.txt', 'r') as f:
result.append(f.read())
print(f"同步耗时: {time.time() - start:.2f}秒")
return result
# 异步版本 - 读取3个文件
import asyncio
import aiofiles
asyncdefasync_read_files():
start = time.time()
asyncdefread_one(i):
asyncwith aiofiles.open(f'file_{i}.txt', 'r') as f:
returnawait f.read()
tasks = [read_one(i) for i in range(3)]
result = await asyncio.gather(*tasks)
print(f"异步耗时: {time.time() - start:.2f}秒")
return result
测试结果大概是酱紫的:
同步耗时: 3.00秒 (每个文件1秒)
异步耗时: 1.00秒 (3个一起读完)
看到没。效率提升3倍。文件越多,提升越明显。
核心三件套,asyncio其实就三个概念
import asyncio
# 1. async def - 定义一个协程函数
asyncdefhello():
print("Hello")
await asyncio.sleep(1)
print("World")
# 2. asyncio.run() - 运行协程
asyncio.run(hello())
# 3. asyncio.gather() - 并发执行多个协程
asyncdefmain():
await asyncio.gather(
hello(),
hello(),
hello(),
)
asyncio.run(main())
实战场景来了,爬虫速度翻10倍
import asyncio
import aiohttp
asyncdeffetch(session, url):
asyncwith session.get(url) as resp:
returnawait resp.text()
asyncdeffetch_all(urls):
asyncwith aiohttp.ClientSession() as session:
tasks = [fetch(session, url) for url in urls]
returnawait asyncio.gather(*tasks)
urls = [f"https://api.example.com/item/{i}"for i in range(100)]
asyncio.run(fetch_all(urls))
这里有个大坑得提醒一下:
# 错误示范 - 请求太多会被封
tasks = [fetch(session, url) for url in range(10000)]
# 正确姿势 - 控制并发数
asyncdeffetch_with_limit(semaphore, session, url):
asyncwith semaphore:
returnawait fetch(session, url)
asyncdeffetch_all_safe(urls):
semaphore = asyncio.Semaphore(10)
asyncwith aiohttp.ClientSession() as session:
tasks = [fetch_with_limit(semaphore, session, url) for url in urls]
returnawait asyncio.gather(*tasks)
异步文件操作,aiofiles是神器
标准库的文件操作是同步的,得用aiofiles来处理异步读写。
import aiofiles
import json
asyncdefwrite_data():
data = {'key': 'value', 'numbers': list(range(1000))}
asyncwith aiofiles.open('data.json', 'w') as f:
await f.write(json.dumps(data))
print("写入完成!")
asyncdefread_data():
asyncwith aiofiles.open('data.json', 'r') as f:
content = await f.read()
return json.loads(content)
asyncdefmain():
await write_data()
data = await read_data()
print(f"读取到 {len(data['numbers'])} 个数字")
asyncio.run(main())
异步Redis和数据库,aiosonic了解一下
# 异步Redis操作
import aioredis
asyncdefasync_redis_demo():
redis = await aioredis.create_redis('redis://localhost')
await redis.set('key', 'value')
value = await redis.get('key')
redis.close()
print(f"获取到: {value.decode()}")
# 异步数据库操作
import asyncpg
asyncdefasync_db_demo():
pool = await asyncpg.create_pool('postgresql://user:pass@localhost/db', max_conn=20)
asyncwith pool.acquire() as conn:
rows = await conn.fetch('SELECT * FROM users WHERE age > $1', 18)
for row in rows:
print(f"用户: {row['name']}, 年龄: {row['age']}")
await pool.close()
混子哥的经验之谈
什么时候用异步?
I/O密集型场景用异步准没错——网络请求、文件读写、数据库操作。CPU密集型。老实去用多进程吧,别折腾异步了。
异步的坑:
性能对比一览
总结
异步编程就三板斧:
记住它是来解决I/O瓶颈的,不是万能药。搞清楚了,效率翻10倍不是梦。