python中多程序協程的使用以及為什麼要用它

2021-12-25 08:57:00 字數 5127 閱讀 9871

1樓:育知同創教育

python裡推薦用多程序而不是多執行緒,但是多程序也有其自己的限制:相比執行緒更加笨重、切換耗時更長,並且在python的多程序下,程序數量不推薦超過cpu核心數(一個程序只有一個gil,所以一個程序只能跑滿一個cpu),因為一個程序佔用一個cpu時能充分利用機器的效能,但是程序多了就會出現頻繁的程序切換,反而得不償失。

不過特殊情況(特指io密集型任務)下,多執行緒是比多程序好用的。

舉個例子:給你200w條url,需要你把每個url對應的頁面抓取儲存起來,這種時候,單單使用多程序,效果肯定是很差的。為什麼呢?

例如每次請求的等待時間是2秒,那麼如下(忽略cpu計算時間):

1、單程序+單執行緒:需要2秒*200w=400w秒==1111.11個小時==46.3天,這個速度明顯是不能接受的

2、單程序+多執行緒:例如我們在這個程序中開了10個多執行緒,比1中能夠提升10倍速度,也就是大約4.63天能夠完成200w條抓取,請注意,這裡的實際執行是:

執行緒1遇見了阻塞,cpu切換到執行緒2去執行,遇見阻塞又切換到執行緒3等等,10個執行緒都阻塞後,這個程序就阻塞了,而直到某個執行緒阻塞完成後,這個程序才能繼續執行,所以速度上提升大約能到10倍(這裡忽略了執行緒切換帶來的開銷,實際上的提升應該是不能達到10倍的),但是需要考慮的是執行緒的切換也是有開銷的,所以不能無限的啟動多執行緒(開200w個執行緒肯定是不靠譜的)

3、多程序+多執行緒:這裡就厲害了,一般來說也有很多人用這個方法,多程序下,每個程序都能佔一個cpu,而多執行緒從一定程度上繞過了阻塞的等待,所以比單程序下的多執行緒又更好使了,例如我們開10個程序,每個程序裡開20w個執行緒,執行的速度理論上是比單程序開200w個執行緒快10倍以上的(為什麼是10倍以上而不是10倍,主要是cpu切換200w個執行緒的消耗肯定比切換20w個程序大得多,考慮到這部分開銷,所以是10倍以上)。

還有更好的方法嗎?答案是肯定的,它就是:

4、協程,使用它之前我們先講講what/why/how(它是什麼/為什麼用它/怎麼使用它)

what:

協程是一種使用者級的輕量級執行緒。協程擁有自己的暫存器上下文和棧。協程排程切換時,將暫存器上下文和棧儲存到其他地方,在切回來的時候,恢復先前儲存的暫存器上下文和棧。因此:

協程能保留上一次呼叫時的狀態(即所有區域性狀態的一個特定組合),每次過程重入時,就相當於進入上一次呼叫的狀態,換種說法:進入上一次離開時所處邏輯流的位置。

在併發程式設計中,協程與執行緒類似,每個協程表示一個執行單元,有自己的本地資料,與其它協程共享全域性資料和其它資源。

why:

目前主流語言基本上都選擇了多執行緒作為併發設施,與執行緒相關的概念是搶佔式多工(preemptive multitasking),而與協程相關的是協作式多工。

不管是程序還是執行緒,每次阻塞、切換都需要陷入系統呼叫(system call),先讓cpu跑作業系統的排程程式,然後再由排程程式決定該跑哪一個程序(執行緒)。而且由於搶佔式排程執行順序無法確定的特點,使用執行緒時需要非常小心地處理同步問題,而協程完全不存在這個問題(事件驅動和非同步程式也有同樣的優點)。

因為協程是使用者自己來編寫排程邏輯的,對cpu來說,協程其實是單執行緒,所以cpu不用去考慮怎麼排程、切換上下文,這就省去了cpu的切換開銷,所以協程在一定程度上又好於多執行緒。

how:

python裡面怎麼使用協程?答案是使用gevent,使用方法:看這裡

使用協程,可以不受執行緒開銷的限制,我嘗試過一次把20w條url放在單程序的協程裡執行,完全沒問題。

所以最推薦的方法,是多程序+協程(可以看作是每個程序裡都是單執行緒,而這個單執行緒是協程化的)

多程序+協程下,避開了cpu切換的開銷,又能把多個cpu充分利用起來,這種方式對於資料量較大的爬蟲還有檔案讀寫之類的效率提升是巨大的。

2樓:匿名使用者

因為python的多執行緒不是真正的多執行緒 還只是一個執行緒在執行

多程序才能發揮多核cpu的效能

python中多程序+協程的使用以及為什麼要用它

3樓:

前面講了為什麼python裡推薦用多程序而不是多執行緒,但是多程序也有其自己的限制:相比執行緒更加笨重、切換耗時更長,並且在python的多程序下,程序數量不推薦超過cpu核心數(一個程序只有一個gil,所以一個程序只能跑滿一個cpu),因為一個程序佔用一個cpu時能充分利用機器的效能,但是程序多了就會出現頻繁的程序切換,反而得不償失。

不過特殊情況(特指io密集型任務)下,多執行緒是比多程序好用的。

舉個例子:給你200w條url,需要你把每個url對應的頁面抓取儲存起來,這種時候,單單使用多程序,效果肯定是很差的。為什麼呢?

例如每次請求的等待時間是2秒,那麼如下(忽略cpu計算時間):

1、單程序+單執行緒:需要2秒*200w=400w秒==1111.11個小時==46.3天,這個速度明顯是不能接受的

2、單程序+多執行緒:例如我們在這個程序中開了10個多執行緒,比1中能夠提升10倍速度,也就是大約4.63天能夠完成200w條抓取,請注意,這裡的實際執行是:

執行緒1遇見了阻塞,cpu切換到執行緒2去執行,遇見阻塞又切換到執行緒3等等,10個執行緒都阻塞後,這個程序就阻塞了,而直到某個執行緒阻塞完成後,這個程序才能繼續執行,所以速度上提升大約能到10倍(這裡忽略了執行緒切換帶來的開銷,實際上的提升應該是不能達到10倍的),但是需要考慮的是執行緒的切換也是有開銷的,所以不能無限的啟動多執行緒(開200w個執行緒肯定是不靠譜的)

3、多程序+多執行緒:這裡就厲害了,一般來說也有很多人用這個方法,多程序下,每個程序都能佔一個cpu,而多執行緒從一定程度上繞過了阻塞的等待,所以比單程序下的多執行緒又更好使了,例如我們開10個程序,每個程序裡開20w個執行緒,執行的速度理論上是比單程序開200w個執行緒快10倍以上的(為什麼是10倍以上而不是10倍,主要是cpu切換200w個執行緒的消耗肯定比切換20w個程序大得多,考慮到這部分開銷,所以是10倍以上)。

還有更好的方法嗎?答案是肯定的,它就是:

4、協程,使用它之前我們先講講what/why/how(它是什麼/為什麼用它/怎麼使用它)

what:

協程是一種使用者級的輕量級執行緒。協程擁有自己的暫存器上下文和棧。協程排程切換時,將暫存器上下文和棧儲存到其他地方,在切回來的時候,恢復先前儲存的暫存器上下文和棧。因此:

在併發程式設計中,協程與執行緒類似,每個協程表示一個執行單元,有自己的本地資料,與其它協程共享全域性資料和其它資源。

why:

目前主流語言基本上都選擇了多執行緒作為併發設施,與執行緒相關的概念是搶佔式多工(preemptive multitasking),而與協程相關的是協作式多工。

不管是程序還是執行緒,每次阻塞、切換都需要陷入系統呼叫(system call),先讓cpu跑作業系統的排程程式,然後再由排程程式決定該跑哪一個程序(執行緒)。

而且由於搶佔式排程執行順序無法確定的特點,使用執行緒時需要非常小心地處理同步問題,而協程完全不存在這個問題(事件驅動和非同步程式也有同樣的優點)。

因為協程是使用者自己來編寫排程邏輯的,對cpu來說,協程其實是單執行緒,所以cpu不用去考慮怎麼排程、切換上下文,這就省去了cpu的切換開銷,所以協程在一定程度上又好於多執行緒。

how:

python裡面怎麼使用協程?答案是使用gevent,使用方法:看這裡

使用協程,可以不受執行緒開銷的限制,我嘗試過一次把20w條url放在單程序的協程裡執行,完全沒問題。

所以最推薦的方法,是多程序+協程(可以看作是每個程序裡都是單執行緒,而這個單執行緒是協程化的)

多程序+協程下,避開了cpu切換的開銷,又能把多個cpu充分利用起來,這種方式對於資料量較大的爬蟲還有檔案讀寫之類的效率提升是巨大的。

小例子:

[python] view plain copy

#-*- coding=utf-8 -*-

import requests

from multiprocessing import process

import gevent

from gevent import monkey; monkey.patch_all()

import sys

reload(sys)

sys.setdefaultencoding('utf8')

def fetch(url):

try:

s = requests.session()

r = s.get(url,timeout=1)#在這裡抓取頁面

except exception,e:

print e

return ''

def process_start(url_list):

tasks =

for url in url_list:

gevent.joinall(tasks)#使用協程來執行

def task_start(filepath,flag = 100000):#每10w條url啟動一個程序

with open(filepath,'r') as reader:#從給定的檔案中讀取url

url = reader.readline().strip()

url_list = #這個list用於存放協程任務

i = 0 #計數器,記錄新增了多少個url到協程佇列

while url!='':

i += 1

if i == flag:#一定數量的url就啟動一個程序並執行

p.start()

url_list =  #重置url佇列

i = 0 #重置計數器

url = reader.readline().strip()

if url_list not :#若退出迴圈後任務佇列裡還有url剩餘

#把剩餘的url全都放到最後這個程序來執行

p.start()

if __name__ == '__main__':

task_start('./testdata.txt')#讀取指定檔案

細心的同學會發現:上面的例子中隱藏了一個問題:程序的數量會隨著url數量的增加而不斷增加,我們在這裡不使用程序池multiprocessing.

pool來控制程序數量的原因是multiprocessing.pool和gevent有衝突不能同時使用,但是有興趣的同學可以研究一下gevent.pool這個協程池。

多核 多cpu 多程序 多執行緒的關係

程式單一化時可以這麼理解,但現在一般是基於作業系統的,所以作業系統會按其設計優化規則去盡力合理排程和分配硬體資源,但是碰到頻繁的跳轉其效率仍然會降低,這裡cpu的快取越大就越能掩蓋這些非優設計。而程式本身如果按多核模式設計的則效率更高,所以表現出來老的程式在新硬體上執行仍然有提高,而針對多核優化設計...

怎樣使用python查詢系統某一程序是否存在

只需要一小段python 就可以解決用python查詢判斷系統程序是否存在的操作。具休是怎麼樣判斷程序是不是存在,看下邊的python 就會明白了。正常我們在編寫python 時,要殺死一個程序之前,一定要做的事情就是要知道這個程序是不是存在,存在要怎麼樣操作 不存在又怎麼樣操作。如何查詢一個程序是...

在python中定義函式python中怎麼呼叫自定義函式

涉及到狀態儲存,可以使用函子 書上這麼翻譯的,不曉得其他人是不是也這樣叫 給你個例子,你比對著改。如果不懂,自己再延這個方向去檢視資料。class strip def init self,characters 初始化,將需要保留的狀態資訊存起來 self.characters characters ...