進程和線程、協程的區別


 現在多進程多線程已經是老生常談了,協程也在最近幾年流行起來。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 import multiprocessing
 3 
 4 def f(x):
 5     return x*x
 6 
 7 def go():
 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 import multiprocessing
19 
20 class someClass(object):
21     def __init__(self):
22         pass
23 
24     def f(self, x):
25         return x*x
26 
27     def go(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 <type 'instancemethod'>: attribute lookup __builtin__.instancemethod failed
出現問題

 

 1 import multiprocessing
 2 
 3 def func(x):
 4     return x*x
 5 
 6 class someClass(object):
 7     def __init__(self,func):
 8         self.f = func
 9 
10     def go(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.py
22 [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密集型一般使用多進程,強調非阻塞異步並發的一般都是使用協程,當然有時候也是需要多進程線程池結合的,或者是其他組合方式。

 

先記錄 未完結!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM