現在多程序多線程已經是老生常談了,協程也在最近幾年流行起來。python中有協程庫gevent,py web架構tornado中也用了gevent封裝好的協程。本文主要介紹程序、線程和協程三者之間的差別。
一、概念
1、程序
程序是具有一定獨立功能的程式關于某個資料集合上的一次運作活動,程序是系統進行資源配置設定和排程的一個獨立機關。每個程序都有自己的獨立記憶體空間,不同程序通過程序間通信來通信。由于程序比較重量,占據獨立的記憶體,是以上下文程序間的切換開銷(棧、寄存器、虛拟記憶體、檔案句柄等)比較大,但相對比較穩定安全。
2、線程
線程是程序的一個實體,是CPU排程和分派的基本機關,它是比程序更小的能獨立運作的基本機關.線程自己基本上不擁有系統資源,隻擁有一點在運作中必不可少的資源(如程式計數器,一組寄存器和棧),但是它可與同屬一個程序的其他的線程共享程序所擁有的全部資源。線程間通信主要通過共享記憶體,上下文切換很快,資源開銷較少,但相比程序不夠穩定容易丢失資料。
3、協程
協程是一種使用者态的輕量級線程,協程的排程完全由使用者控制。協程擁有自己的寄存器上下文和棧。協程排程切換時,将寄存器上下文和棧儲存到其他地方,在切回來的時候,恢複先前儲存的寄存器上下文和棧,直接操作棧則基本沒有核心切換的開銷,可以不加鎖的通路全局變量,是以上下文的切換非常快。
二、差別:
1、程序多與線程比較
線程是指程序内的一個執行單元,也是程序内的可排程實體。線程與程序的差別:
1) 位址空間:線程是程序内的一個執行單元,程序内至少有一個線程,它們共享程序的位址空間,而程序有自己獨立的位址空間
2) 資源擁有:程序是資源配置設定和擁有的機關,同一個程序内的線程共享程序的資源
3) 線程是處理器排程的基本機關,但程序不是
4) 二者均可并發執行
5) 每個獨立的線程有一個程式運作的入口、順序執行序列和程式的出口,但是線程不能夠獨立執行,必須依存在應用程式中,由應用程式提供多個線程執行控制
2、協程多與線程進行比較
1) 一個線程可以多個協程,一個程序也可以單獨擁有多個協程,這樣python中則能使用多核CPU。
2) 線程程序都是同步機制,而協程則是異步
3) 協程能保留上一次調用時的狀态,每次過程重入時,就相當于進入上一次調用的狀态
三、程序和線程、協程在python中的使用的例子
多程序一般使用multiprocessing庫,來利用多核CPU,主要是用在CPU密集型的程式上,當然生産者消費者這種也可以使用。多程序的優勢就是一個子程序崩潰并不會影響其他子程序和主程序的運作,但缺點就是不能一次性啟動太多程序,會嚴重影響系統的資源排程,特别是CPU使用率和負載。注:python2的程序池在類中的使用會有問題,需要把類函數定義成全局函數。
1 接觸到python多程序的一個典型的例子如下!2 importmultiprocessing3
4 deff(x):5 return x*x6
7 defgo():8 pool = multiprocessing.Pool(processes=4)9 #result = pool.apply_async(self.f, [10])
10 #print result.get(timeout=1)
11 print pool.map(f, range(10))12
13
14 if __name__== '__main__':15 go()16 可是,一旦加入了class,程式就顯示錯誤。程式和結果如下:17 程式:18 importmultiprocessing19
20 classsomeClass(object):21 def __init__(self):22 pass
23
24 deff(self, x):25 return x*x26
27 defgo(self):28 pool = multiprocessing.Pool(processes=4)29 #result = pool.apply_async(self.f, [10])
30 #print result.get(timeout=1)
31 print pool.map(self.f, range(10))32
33 結果:34 PicklingError: Can't pickle : attribute lookup __builtin__.instancemethod failed
出現問題
1 importmultiprocessing2
3 deffunc(x):4 return x*x5
6 classsomeClass(object):7 def __init__(self,func):8 self.f =func9
10 defgo(self):11 pool = multiprocessing.Pool(processes=4)12 #result = pool.apply_async(self.f, [10])
13 #print result.get(timeout=1)
14 print pool.map(self.f, range(10))15
16 a=someClass(func)17 a.go()18
19 #===========列印結果==============
20
21 $python f.py22 [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
解決上述問題
總結:
我不是太了解map的過程,但是它必須把self對象傳遞到各個程序中才行,由于map隻需要一個參數,self多了出來。我看了《Python标準庫》,這裡介紹的是它上的做法。
至于為什麼報pickle的錯,我也不明白。
四、程序和線程、協程在python中的使用
2、多線程一般是使用threading庫,完成一些IO密集型并發操作。多線程的優勢是切換快,資源消耗低,但一個線程挂掉則會影響到所有線程,是以不夠穩定。現實中使用線程池的場景會比較多,具體可參考https://www.cnblogs.com/rianley/p/9076207.html
3、協程一般是使用gevent庫,當然這個庫用起來比較麻煩,是以使用的并不是很多。相反,協程在tornado的運用就多得多了,使用協程讓tornado做到單線程異步,據說還能解決C10K的問題。是以協程使用的地方最多的是在web應用上。
總結一下就是IO密集型一般使用多線程或者多程序,CPU密集型一般使用多程序,強調非阻塞異步并發的一般都是使用協程,當然有時候也是需要多程序線程池結合的,或者是其他組合方式。
先記錄 未完結!