Python中实现协程依赖async/await语法和asyncio库,通过事件循环调度,实现单线程内高效并发处理I/O密集型任务。使用async def定义协程函数,await暂停执行并让出控制权,避免阻塞。相比多线程和多进程,协程开销小、调度由程序控制,适合高并发I/O场景,但需避免阻塞调用。常见实践包括使用aiohttp等异步库、asyncio.gather并发执行、asyncio.create_task管理后台任务,并注意异常处理与共享状态同步。底层基于生成器和事件循环机制,通过await/Future实现非阻塞I/O操作。

Python中实现协程,核心在于利用
async/await
asyncio
在Python中实现协程编程,我们主要依赖于内置的
asyncio
首先,你需要使用
async def
import asyncio
async def my_first_coroutine():
print("协程开始了...")
# 模拟一个耗时的I/O操作,例如网络请求或数据库查询
# 注意这里使用的是 asyncio.sleep,而不是 time.sleep
# time.sleep 会阻塞整个事件循环,而 asyncio.sleep 会让出控制权
await asyncio.sleep(1) # 暂停1秒,期间事件循环可以运行其他任务
print("协程执行完毕!")
async def another_coroutine():
print("这是另一个协程,它在等待时也能运行。")
await asyncio.sleep(0.5)
print("另一个协程也完成了。")
async def main():
# 使用 await 关键字来等待一个协程的完成
await my_first_coroutine()
await another_coroutine()
# 如果你想并发运行多个协程,可以使用 asyncio.gather
print("\n--- 并发运行多个协程 ---")
await asyncio.gather(
my_first_coroutine(),
another_coroutine()
)
print("所有并发协程都已完成。")
if __name__ == "__main__":
# 运行主协程,这会启动事件循环并执行所有排队的协程
asyncio.run(main())这段代码展示了协程的基本结构。
asyncio.run(main())
main
main
await
await
asyncio.gather
立即学习“Python免费学习笔记(深入)”;
谈到并发,很多人首先想到的是多线程或多进程。但协程提供了一种截然不同的并发模型,理解它们之间的差异对于选择合适的工具至关重要。
对我来说,最大的区别在于调度方式和资源开销。
多进程(Multiprocessing):
多线程(Multithreading):
协程(Coroutines):
await
await
简单来说,如果你的任务是计算密集型的,需要充分利用多核CPU,那么多进程是首选。如果你的任务是I/O密集型,并且需要轻量级的并发,那么协程无疑是更优雅、更高效的选择。多线程在Python中介于两者之间,在I/O密集型任务中也有一定作用,但往往伴随着更复杂的同步问题。
即便协程编程强大而优雅,它也有自己的“脾气”。我在实践中遇到过一些坑,也总结了一些经验,分享给你。
常见陷阱:
阻塞了事件循环:这是最致命的错误。在协程中,你绝对不能使用任何会阻塞当前线程的同步调用,比如
time.sleep()
requests.get()
asyncio
asyncio.sleep
asyncio
aiohttp
requests
aiosqlite
sqlite3
run_in_executor
忘记await
async def
await
await
asyncio.create_task
async def
await
asyncio.create_task
不当的异常处理:在异步任务中,如果一个任务抛出异常但没有被捕获,它可能会默默地导致整个程序崩溃,或者只是让那个任务失败而你却不知道。
try...except
asyncio.gather
asyncio.create_task
return_exceptions=True
task.add_done_callback
共享状态的竞态条件:虽然协程是单线程的,但如果你有多个并发运行的协程修改同一个共享变量,仍然可能出现竞态条件。例如,两个协程同时尝试递增一个计数器,由于
await
asyncio.Lock
asyncio.Semaphore
最佳实践:
asyncio.gather
asyncio.gather
asyncio.create_task
create_task
asyncio.CancelledError
Python异步编程在处理并发I/O操作方面简直是如鱼得水,这正是它的设计初衷和最大优势所在。理解其工作机制,能让你更高效地构建高性能网络应用。
本书是全面讲述PHP与MySQL的经典之作,书中不但全面介绍了两种技术的核心特性,还讲解了如何高效地结合这两种技术构建健壮的数据驱动的应用程序。本书涵盖了两种技术新版本中出现的最新特性,书中大量实际的示例和深入的分析均来自于作者在这方面多年的专业经验,可用于解决开发者在实际中所面临的各种挑战。 本书内容全面深入,适合各层次PHP和MySQL开发人员阅读,既是优秀的学习教程,也可用作参考手册。
253
核心思想是非阻塞I/O和事件循环。
当一个协程发起一个I/O操作(比如向服务器发送一个HTTP请求)时,它不会傻傻地原地等待响应。相反,它会立即将这个I/O请求提交给操作系统,然后await
await
此时,事件循环会接收到控制权,它会检查是否有其他已经准备好运行的协程,或者是否有之前发起的I/O操作已经完成。它会不断地轮询或被操作系统通知(通过像epoll、kqueue这样的机制),哪个I/O操作已经有了结果。
一旦之前那个协程所等待的I/O操作(比如HTTP响应已经回来了)完成,事件循环就会将控制权交还给那个协程,让它从
await
这个过程是如此的轻量和高效,因为它避免了线程或进程切换带来的巨大开销。所有这些“并发”都发生在一个线程内,通过巧妙地调度和切换任务上下文来实现。
举个例子:并发下载网页
假设你需要从多个网站下载内容,传统的同步方式是一个接一个地下载,效率低下。使用异步编程,你可以同时发起所有下载请求,然后等待它们全部完成。
import asyncio
import aiohttp # 这是一个异步HTTP客户端库
async def fetch_url(session, url):
print(f"开始下载: {url}")
try:
async with session.get(url) as response:
# await response.text() 也是一个I/O操作,会暂停当前协程
content = await response.text()
print(f"完成下载: {url}, 内容长度: {len(content)} 字符")
return f"URL: {url}, Status: {response.status}, Length: {len(content)}"
except aiohttp.ClientError as e:
print(f"下载失败: {url}, 错误: {e}")
return f"URL: {url}, Error: {e}"
async def main_downloader():
urls = [
"http://example.com",
"http://python.org",
"http://www.google.com",
"http://nonexistent-domain-12345.com" # 故意放一个会失败的
]
async with aiohttp.ClientSession() as session: # 创建一个HTTP会话
# 使用 asyncio.gather 来并发运行所有 fetch_url 协程
# gather 会等待所有协程都完成后才返回
results = await asyncio.gather(*[fetch_url(session, url) for url in urls])
print("\n--- 所有下载任务完成 ---")
for result in results:
print(result)
if __name__ == "__main__":
asyncio.run(main_downloader())在这个例子中:
main_downloader
aiohttp.ClientSession
urls
fetch_url
asyncio.gather(*[...])
fetch_url
fetch_url
await session.get(url)
await response.text()
fetch_url
fetch_url
asyncio.gather
main_downloader
这种模式极大地提高了I/O密集型应用的吞吐量和响应速度,因为程序不再需要为每个I/O操作分配一个独立的线程或进程,也避免了因等待I/O而导致的CPU空闲。
要深入理解协程,我们得稍微触及一下它的底层机制。这其实并非魔法,而是Python在语言层面和库层面做出的巧妙设计。
早期的Python协程(在
async/await
yield from
yield
next()
yield from
asyncio
yield from
随着Python 3.5引入
async/await
async def
具体来说:
async def
async def
async def
await
await some_awaitable
await
some_awaitable
some_awaitable
事件循环(Event Loop):
asyncio
await
epoll
kqueue
IOCP
await
Future对象:在
asyncio
Future
Future
Future
await
Future
所以,从本质上讲,
async/await
以上就是Python中协程如何实现 Python中协程编程教程的详细内容,更多请关注php中文网其它相关文章!
编程怎么学习?编程怎么入门?编程在哪学?编程怎么学才快?不用担心,这里为大家提供了编程速学教程(入门课程),有需要的小伙伴保存下载就能学习啦!
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号