@Author: runsen
協(xié)程是實(shí)現(xiàn)并發(fā)編程的一種方式腊尚。
https://docs.python.org/zh-cn/3/library/asyncio.html
一說并發(fā),你肯定想到了多線程 满哪, 多進(jìn)程模型婿斥,沒錯(cuò),多線程 和 多進(jìn)程哨鸭,正是解決并發(fā)問題的經(jīng)典模型之一
但是你了解過協(xié)程Coroutine嗎民宿?
協(xié)程:是單線程下的并發(fā),又稱微線程像鸡。
就是只有一個(gè)線程活鹰,如何提高速度,解決并發(fā)編程
英文名Coroutine坟桅。
協(xié)程比線程的單位更小——協(xié)程
注意協(xié)程這個(gè)概念完全是程序員自己想出來的東西华望,它對于操作系統(tǒng)來說根本不存在。操作系統(tǒng)只有進(jìn)程和線程仅乓。
從一個(gè)demo學(xué)起
import time
def print_num(num):
print("Maoli is printing " + str(num) + " nows" )
time.sleep(1)
print("Maoli prints" + str(num) + " OK")
def main(nums):
for num in nums:
print_num(num)
%time main([i for i in range(1,6)])
Maoli is printing 1 nows
Maoli prints1 OK
Maoli is printing 2 nows
Maoli prints2 OK
Maoli is printing 3 nows
Maoli prints3 OK
Maoli is printing 4 nows
Maoli prints4 OK
Maoli is printing 5 nows
Maoli prints5 OK
Wall time: 5 s
%time 需要在jupyter notebook中運(yùn)行赖舟。
上面代碼是從上到下執(zhí)行的。
下面將上面代碼改為協(xié)程版
注意py版本3.7以上夸楣,主要使用的是asyncio
import asyncio
async def print_num(num):
print("Maoli is printing " + str(num) + " nows" )
await asyncio.sleep(1)
print("Maoli prints" + str(num) + " OK")
async def main(nums):
for num in nums:
await print_num(num)
%time asyncio.run(main([i for i in range(1,6)]))
Maoli is printing 1 nows
Maoli prints1 OK
Maoli is printing 2 nows
Maoli prints2 OK
Maoli is printing 3 nows
Maoli prints3 OK
Maoli is printing 4 nows
Maoli prints4 OK
Maoli is printing 5 nows
Maoli prints5 OK
Wall time: 5.01 s
asyncio.run() 函數(shù)用來運(yùn)行最高層級(jí)的入口點(diǎn) "main()" 函數(shù)
await 是同步調(diào)用等待一個(gè)協(xié)程宾抓。以下代碼段會(huì)在等待 1 秒后打印 num,速度上沒有發(fā)生改變豫喧。
需要引入asyncio.create_task才可以
可等待對象
如果一個(gè)對象可以在 await 語句中使用石洗,那么它就是 可等待 對象
協(xié)程中的還一個(gè)重要概念,任務(wù)(Task)
如果寫一個(gè)數(shù)字是一個(gè)任務(wù)紧显,那么毛利我要完成5個(gè)任務(wù)
毛利我寫個(gè)1-5都這么慢讲衫,不行,我要加速寫
asyncio.create_task() 函數(shù)用來并發(fā)運(yùn)行作為 asyncio 任務(wù) 的多個(gè)協(xié)程孵班。
import asyncio
async def print_num(num):
print("Maoli is printing " + str(num) + " nows" )
await asyncio.sleep(1)
print("Maoli prints" + str(num) + " OK")
async def main(nums):
tasks = [asyncio.create_task(print_num(num)) for num in nums]
for task in tasks:
await task
%time asyncio.run(main([i for i in range(1,6)]))
Maoli is printing 1 nows
Maoli is printing 2 nows
Maoli is printing 3 nows
Maoli is printing 4 nows
Maoli is printing 5 nows
Maoli prints1 OK
Maoli prints3 OK
Maoli prints5 OK
Maoli prints2 OK
Maoli prints4 OK
Wall time: 1.01 s
還可以寫成await asyncio.gather(*tasks)
這種方法
import asyncio
async def print_num(num):
print("Maoli is printing " + str(num) + " nows" )
await asyncio.sleep(1)
print("Maoli prints" + str(num) + " OK")
async def main(nums):
tasks = [asyncio.create_task(print_num(num)) for num in nums]
await asyncio.gather(*tasks)
%time asyncio.run(main([i for i in range(1,6)]))
*tasks 解包列表涉兽,將列表變成了函數(shù)的參數(shù);與之對應(yīng)的是篙程, ** dict 將字典變成了函數(shù)的參數(shù)枷畏。
asyncio 隊(duì)列
asyncio也是只有在Pytohn3.7才有的東西。
asyncio 隊(duì)列被設(shè)計(jì)成與 queue 模塊類似虱饿。
import asyncio
import random
async def consumer(queue, id):
while True:
val = await queue.get()
print('{} get a val: {}'.format(id, val))
await asyncio.sleep(1)
async def producer(queue, id):
for i in range(5):
val = random.randint(1, 10)
await queue.put(val)
print('{} put a val: {}'.format(id, val))
await asyncio.sleep(1)
async def main():
# 創(chuàng)建隊(duì)列
queue = asyncio.Queue()
# 消費(fèi)者1號(hào)
consumer_1 = asyncio.create_task(consumer(queue, 'consumer_1'))
# 消費(fèi)者2號(hào)
consumer_2 = asyncio.create_task(consumer(queue, 'consumer_2'))
# 生產(chǎn)者1號(hào)
producer_1 = asyncio.create_task(producer(queue, 'producer_1'))
# 生產(chǎn)者2號(hào)
producer_2 = asyncio.create_task(producer(queue, 'producer_2'))
# stop 10秒
await asyncio.sleep(10)
consumer_1.cancel()
consumer_2.cancel()
await asyncio.gather(consumer_1, consumer_2, producer_1, producer_2, return_exceptions=True)
%time asyncio.run(main())
協(xié)程的寫法簡潔清晰拥诡,只要把 async / await 語法和 create_task 結(jié)合來用触趴,就是Python中比較常見的協(xié)程
如果不會(huì)多線程,看我之前文章