python 協程 and 程序

2022-02-05 22:13:55 字數 3433 閱讀 1195

先從乙個爬蟲開始,請看下面的**

import time

def crawl_page(url):

print('crawling {}'.format(url))

sleep_time = int(url.split('_')[-1])

time.sleep(sleep_time)

print('ok {}'.format(url))

def main(urls):

for url in urls:

crawl_page(url)

%time main(['url_1', 'url_2', 'url_3', 'url_4'])

########## 輸出 ##########

crawling url_1

ok url_1

crawling url_2

ok url_2

crawling url_3

ok url_3

crawling url_4

ok url_4

wall time: 10 s

這是乙個很簡單的爬蟲,main() 函式執行時,調取 crawl_page() 函式進行網路通訊,經過若干秒等待後收到結果,然後執行下乙個。

看起來很簡單,但你仔細一算,它也占用了不少時間,五個頁面分別用了 1 秒到 4 秒的時間,加起來一共用了 10 秒。這顯然效率低下,該怎麼優化呢?

於是,乙個很簡單的思路出現了——我們這種爬取操作,完全可以併發化。我們就來看看使用協程怎麼寫。

import asyncio

async def crawl_page(url):

print('crawling {}'.format(url))

sleep_time = int(url.split('_')[-1])

await asyncio.sleep(sleep_time)

print('ok {}'.format(url))

async def main(urls):

for url in urls:

await crawl_page(url)

%time asyncio.run(main(['url_1', 'url_2', 'url_3', 'url_4']))

########## 輸出 ##########

crawling url_1

ok url_1

crawling url_2

ok url_2

crawling url_3

ok url_3

crawling url_4

ok url_4

wall time: 10 s

首先來看 import asyncio,這個庫包含了大部分我們實現協程所需的魔法工具。

async 修飾詞宣告非同步函式,於是,這裡的 crawl_page 和 main 都變成了非同步函式。而呼叫非同步函式,我們便可得到乙個協程物件(coroutine object)。

舉個例子,如果你 print(crawl_page('')),便會輸出,提示你這是乙個 python 的協程物件,而並不會真正執行這個函式。

再來說說協程的執行。執行協程有多種方法,這裡我介紹一下常用的三種。

首先,我們可以通過 await 來呼叫。await 執行的效果,和 python 正常執行是一樣的,也就是說程式會阻塞在這裡,進入被呼叫的協程函式,執行完畢返回後再繼續,而這也是 await 的字面意思。**中 await asyncio.sleep(sleep_time) 會在這裡休息若干秒,await crawl_page(url) 則會執行 crawl_page() 函式

import asyncio

async def crawl_page(url):

print('crawling {}'.format(url))

sleep_time = int(url.split('_')[-1])

await asyncio.sleep(sleep_time)

print('ok {}'.format(url))

async def main(urls):

tasks = [asyncio.create_task(crawl_page(url)) for url in urls]

for task in tasks:

await task

%time asyncio.run(main(['url_1', 'url_2', 'url_3', 'url_4']))

########## 輸出 ##########

crawling url_1

crawling url_2

crawling url_3

crawling url_4

ok url_1

ok url_2

ok url_3

ok url_4

wall time: 3.99 s

我們有了協程物件後,便可以通過 asyncio.create_task 來建立任務。

任務建立後很快就會被排程執行,這樣,我們的**也不會阻塞在任務這裡。

所以,我們要等所有任務都結束才行,用for task in tasks: await task 即可。

這次,你就看到效果了吧,結果顯示,執行總時長等於執行時間最長的爬蟲。

當然,你也可以想一想,這裡用多執行緒應該怎麼寫?而如果需要爬取的頁面有上萬個又該怎麼辦呢?再對比下協程的寫法,誰更清晰自是一目了然。其實,對於執行 tasks,還有另一種做法:

import asyncio

async def crawl_page(url):

print('crawling {}'.format(url))

sleep_time = int(url.split('_')[-1])

await asyncio.sleep(sleep_time)

print('ok {}'.format(url))

async def main(urls):

tasks = [asyncio.create_task(crawl_page(url)) for url in urls]

await asyncio.gather(*tasks)

%time asyncio.run(main(['url_1', 'url_2', 'url_3', 'url_4']))

########## 輸出 ##########

crawling url_1

crawling url_2

crawling url_3

crawling url_4

ok url_1

ok url_2

ok url_3

ok url_4

wall time: 4.01 s

```1

Python 程序 執行緒 協程

程序和執行緒之間的關係 執行緒是屬於程序的,執行緒執行在程序空間內,同一程序所產生的執行緒共享同一記憶體空間,當程序退出時該程序所產生的執行緒都會被強制退出並清除。執行緒可與屬於同一程序的其它執行緒共享程序所擁有的全部資源,但是其本身基本上不擁有系統資源,只擁有一點在執行中必不可少的資訊 如程式計數...

Python 程序,執行緒, 協程

程序是系統進行資源分配和排程的乙個獨立單位 最小單位 程序的幾個狀態 空 新建 建立執行乙個程式的新程序,可能的事件有 新的批處理作業 互動登入 終端使用者登入到系統 作業系統因為提供一項服務而建立 由現有的程序派生等。新建 就緒 作業系統準備好再接納乙個程序時,把乙個程序從新建態轉換為就緒態。就緒...

python 執行緒程序協程(二)

1.程序的基本使用 from multiprocessing import process class myprocess process def run self print name pid format self.name,self.pid if name main p myprocess p...