在 Python 中,協程(Coroutine)是一種輕量級的並發程式設計方式,可以透過協作式多任務來實現高效的並發執行。使用 yield 關鍵字掛起函數的執行,以及儲存目前執行狀態,是協程的特殊之處。因此,協程可視為一種特殊的生成器函數。當協程被掛起時,可以使用 send 方法來恢復其執行,並在恢復後傳回一個值。
在 Python 3.4 以前,常使用 yield 關鍵字來實現協程,即稱為「生成器協程」。在 Python 3.4 引入了 asyncio 模組後,可以使用 async/await 關鍵字來定義協程函數,稱為「原生協程」。
協程相比於執行緒和進程,具有以下優點:
輕量級:協程的上下文切換成本很小,可以在單執行緒內並發執行大量的協程。
低延遲:協程的執行過程中,沒有執行緒切換的開銷,也沒有加鎖解鎖的開銷,可以更快地回應外部事件。
高效能:協程的程式碼通常比多執行緒和多進程的程式碼更簡潔可讀,維護成本更低。
協程的使用場景包括網路程式設計、非同步 I/O、資料流處理、高並發任務等。
在 Python 3 中,生成器協程(Generator Coroutine)是指使用生成器函數來實現的協程。生成器函數是一種特殊的函數,其傳回一個生成器對象,可以透過 yield 語句暫停函數的執行,然後在下次呼叫生成器物件的 「next」() 方法時繼續執行。
下面給出一個簡單的生成器協程的範例,其中包含一個生成器函數coroutine 和一個簡單的非同步I/O 操作:
import asyncio def coroutine(): print('Coroutine started') while True: result = yield print('Coroutine received:', result) async def main(): print('Main started') c = coroutine() next(c) c.send('Hello') await asyncio.sleep(1) c.send('World') print('Main finished') asyncio.run(main())
結果輸出:
[root@workhost k8s]# python3 test.py
Main started
Coroutine started
Coroutine received: Hello
Coroutine received: World
Main finished
##4
main 函數開始執行,列印出Main started。
#來看一下,上面程式碼的執行過程:
原生協程
下面給出一個簡單的原生協程範例,其中包含一個async 關鍵字修飾的協程函數coroutine 和一個簡單的非同步I/O 操作:
import asyncio async def coroutine(): print('Coroutine started') await asyncio.sleep(1) print('Coroutine finished') async def main(): print('Main started') await coroutine() print('Main finished') asyncio.run(main())
Python 3 中,原生协程和生成器协程是不同的协程实现方式,它们分别具有独特的特点和适用场景。下面,通过对比它们的区别和优缺点,才可以更好地理解它们之间的异同,以便选择适合自己的协程实现方式,从而更好地编写高效、可维护的异步程序。
1.区别:
定义方式不同:原生协程使用 async/await 关键字来定义,而生成器协程使用 yield 关键字来定义。
返回方式不同:原生协程使用 return 语句来返回结果,而生成器协程使用 yield 语句来返回结果。
调用方式不同:原生协程使用 await 关键字来调用,而生成器协程使用 yield from 或 yield 语句来调用。
原生协程与生成器协程的实现方式不同,前者使用 asyncio 库,后者则是 Python 语言内置的特性。
2.优缺点:
原生协程的优点:
代码简洁易懂:使用 async/await 关键字,可以编写出更简洁易懂的协程代码。
性能更高:原生协程不需要创建生成器对象,也不需要通过 yield 语句来控制函数的执行流程,因此能够更加高效地处理异步操作。
支持异步 I/O 和任务处理:原生协程可以支持异步 I/O 操作和并发任务处理,可以在处理异步操作时更加灵活。
原生协程的缺点:
兼容性差:原生协程是 Python 3.5 版本之后才引入的新特性,因此在旧版本的 Python 中无法使用。
异常处理不方便:原生协程在处理异常时比较麻烦,需要使用 try/except 语句来处理。
生成器协程的优点:
兼容性好:生成器协程是 Python 2 和 Python 3 都支持的特性。
可读性好:生成器协程使用 yield 关键字来实现,代码逻辑清晰易懂。
异常处理方便:生成器协程在处理异常时比较方便,可以使用 try/except 语句来处理。
生成器协程的缺点:
性能相对较低:生成器协程需要创建生成器对象,也需要通过 yield 语句来控制函数的执行流程,因此处理异步操作时性能相对较低。
功能有限:生成器协程不能像原生协程一样支持异步 I/O 操作和任务处理。
接下来,模拟一个场景,假设实现一个异步的批量处理任务的工具,使用原生协程来实现。
看下面代码:
import asyncio import random async def batch_process_task(tasks, batch_size=10): # 将任务列表划分为多个批次 for i in range(0, len(tasks), batch_size): batch = tasks[i:i+batch_size] # 使用原生协程来异步处理每个批次的任务 await asyncio.gather(*[process_task(task) for task in batch]) async def process_task(task): # 模拟任务处理过程 await asyncio.sleep(random.uniform(0.5, 2.0)) print("Task {} processed".format(task)) async def main(): # 构造任务列表 tasks = [i for i in range(1, 101)] # 并发处理批量任务 await batch_process_task(tasks, batch_size=10) if __name__ == '__main__': asyncio.run(main())
输出:
[root@workhost k8s]# python3 test.py
Task 9 processed
Task 10 processed
Task 1 processed
Task 8 processed
Task 6 processed
Task 4 processed
Task 3 processed
Task 2 processed
Task 5 processed
...
...
batch_process_task函数使用原生协程来处理每个批次的任务,而process_task函数则是处理每个任务的函数。在main函数中,任务列表会被构造,并使用batch_process_task函数来异步地处理批量任务。
以上是Python協程的實作方式有哪些的詳細內容。更多資訊請關注PHP中文網其他相關文章!