線程與進程的不同之處在於,它們共用狀態、記憶體和資源。對於線程來說,這個簡單的區別既是它的優勢,又是它的缺點。一方面,線程是輕量級的,並且相互之間易於通訊,但另一方面,它們也帶來了包括死結、競爭條件和高複雜性在內的各種問題。幸運的是,由於 GIL 和隊列模組,與採用其他的語言相比,採用 Python 語言線上程實現的複雜性上要低得多。
全域解譯器鎖 (Global Interpretor Lock) 說明 Python 解譯器並不是安全執行緒的。當前線程必須持有全域鎖,以便對 Python 對象進行安全地訪問。因為只有一個線程可以獲得 Python 對象/C API,所以解譯器每經過 100 個位元組碼的指令,就有規律地釋放和重新獲得鎖。解譯器對線程切換進行檢查的頻率可以通過 sys.setcheckinterval() 函數來進行控制。
需要說明的是,因為 GIL,CPU 受限的應用程式將無法從線程的使用中受益。使用 Python 時,建議使用進程,或者混合建立進程和線程。
Python中如果要使用線程的話,python的lib中提供了兩種方式。一種是函數式,一種是用類來封裝的線程對象。
關於多線程編程的其他知識例如互斥、訊號量、臨界區等請參考python的文檔及相關資料。
下面來看一下使用python進行多線程編程的兩個方式
函數式
調用thread模組中的start_new_thread()函數來產生新的線程,範例程式碼如下:
#!/usr/bin/env python
# encoding: utf8
import time
import thread
def timer(no, interval):
""""""
print 'Thread :(%d) Time:%s'%(no,time.ctime())
time.sleep(interval)
print 'Thread :(%d) End.'%no
def test():
""""""
thread.start_new_thread(timer,(1,1))
thread.start_new_thread(timer,(2,3))
time.sleep(5)
if __name__ == '__main__':
test()
這個是thread.start_new_thread(function,args[,kwargs])函數原型,其中function參數是你將要調用的線程函數;args是講傳遞給你的線程函數的參數,他必須是個tuple類型;而kwargs是可選的參數。 線程的結束一般依靠線程函數的自然結束;也可以線上程函數中調用thread.exit(),他拋出SystemExit exception,達到退出線程的目的。
繼承式
通過調用threading模組繼承threading.Thread類來封裝一個線程對象。範例程式碼如下:
#!/usr/bin/env python
# encoding: utf8
import threading
import time
class timer(threading.Thread): #我的timer類繼承自threading.Thread類
def __init__(self,no,interval):
#在我重寫__init__方法的時候要記得調用基類的__init__方法
threading.Thread.__init__(self)
self.no=no
self.interval=interval
def run(self): #重寫run()方法,把自己的線程函數的代碼放到這裡
print 'Thread Object %s (%d), Time:%s'%(self.getName(),self.no,time.ctime())
time.sleep(self.interval)
def test():
threadone=timer(1,1) #產生2個線程對象
threadtwo=timer(2,3)
threadone.start() #通過調用線程對象的.start()方法來啟用線程
threadtwo.start()
if __name__=='__main__':
test()
多線程分析
getName()是threading.Thread類的一個方法,用來獲得這個線程對象的name。還有一個方法setName()當然就是來設定這個線程對象的name的了。
- 建立一個線程,首先就要先建立一個線程對象:threadone=timer(1,1) 一個線程對象被建立後,他就處於“born”(誕生狀態)
- 啟動線程,調用線程對象的start()方法即可:mythread1.start() 現線上程就處於“ready”狀態或者也稱為“runnable”狀態。
- 線程在未分到CPU時間片處理時處於“sleep”狀態
- 一般來說當線程對象的run方法執行結束或者在執行中拋出異常的話,那麼這個線程就會結束了,處於“dead”狀態。系統會自動對“dead”狀態線程進行清理。
- 如果一個線程t1在執行的過程中需要等待另一個線程t2執行結束後才能啟動並執行話那就可以在t1中調用t2的join()方法
- 如果一個進程的主線程運行完畢而子線程還在執行的話,那麼進程就不會退出,直到所有子線程結束為止。
如何讓主線程結束的時候其他子線程也退出呢?使用線程對象的setDaemon()方法,參數為bool型。True的話就代表你要聽話,我老大(主線程)扯呼,你也要跟著撤,不能拖後腿。如果是False的話就不用那麼聽話了,老大允許你們將在外軍命有所不受的。需要注意的是setDaemon()方法必須線上程對象沒有調用start()方法之前調用,否則沒效果。
t1 = mythread('t1')
print t1.getName(),t1.isDaemon()
t1.setDaemon(True)
print t1.getName(),t1.isDaemon()
t1.start()
print 'main thread exit'
獲得與線程有關的資訊
a.獲得當前正在啟動並執行線程的引用
running = threading.currentThread()
b.獲得當前所有使用中的物件(即run方法開始但是未終止的任何線程)的一個列表
threadlist = threading.enumerate()
c.獲得這個列表的長度
threadcount = threading.activeCount()
d.查看一個線程對象的狀態調用這個線程對象的isAlive()方法,返回1代表處於“runnable”狀態且沒有“dead”
threadflag = threading.isAlive()