(轉)有關thread線程

Python 標準庫提供了 thread 和 threading 兩個模塊來對多線程進行支持。其中, thread 模塊以低級、原始的方式來處理和控制線程,而 threading 模塊經過對 thread 進行二次封裝,提供了更方便的 api 來處理線程。 雖然使用 thread 沒有 threading 來的方便,但它更靈活。python

 

先看一段代碼:api

 1 #coding=gbk
 2 import thread, time, random
 3 count = 0
 4 def threadTest():
 5     global count
 6     for i in xrange(10000):
 7         count += 1
 8 for i in range(10):
 9     thread.start_new_thread(threadTest, ())    #若是對start_new_thread函數不是很瞭解,不要着急,立刻就會講解
10 time.sleep(3)
11 print count    #count是多少呢?是10000 * 10 嗎?

 

thread.start_new_thread function args [ , kwargs ] )

函數將建立一個新的線程,並返回該線程的標識符(標識符爲整數)。參數 function 表示線程建立以後,當即執行的函數,參數 args 是該函數的參數,它是一個元組類型;第二個參數 kwargs 是可選的,它爲函數提供了命名參數字典。函數執行完畢以後,線程將自動退出。若是函數在執行過程當中遇到未處理的異常,該線程將退出,但不會影響其餘線程的執行。 多線程

一個簡單的例子:併發

#coding=gbk
import thread, time
def threadFunc(a = None, b = None, c = None, d = None):
    print time.strftime('%H:%M:%S', time.localtime()), a
    time.sleep(1)    
    print time.strftime('%H:%M:%S', time.localtime()), b
    time.sleep(1)
    print time.strftime('%H:%M:%S', time.localtime()), c
    time.sleep(1)
    print time.strftime('%H:%M:%S', time.localtime()), d
    time.sleep(1)
    print time.strftime('%H:%M:%S', time.localtime()), 'over'
 
thread.start_new_thread(threadFunc, (3, 4, 5, 6))    #建立線程,並執行threadFunc函數。
time.sleep(5)

thread.exit ()

結束當前線程。調用該函數會觸發 SystemExit 異常,若是沒有處理該異常,線程將結束。dom

thread.get_ident ()

返回當前線程的標識符,標識符是一個非零整數。ide

thread.interrupt_main ()

在主線程中觸發 KeyboardInterrupt 異常。子線程可使用該方法來中斷主線程。下面的例子演示了在子線程中調用 interrupt_main ,在主線程中捕獲異常 :函數

import thread, time
thread.start_new_thread(lambda : (thread.interrupt_main(), ), ())
try:
    time.sleep(2)
except KeyboardInterrupt, e:
    print 'error:', e
print 'over'

下面介紹 thread 模塊中的瑣,瑣能夠保證在任什麼時候刻,最多隻有一個線程能夠訪問共享資源。ui

thread.LockType 是 thread 模塊中定義的瑣類型。它有以下方法:spa

lock.acquire ( [ waitflag ] )

獲取瑣。函數返回一個布爾值,若是獲取成功,返回 True ,不然返回 False 。參數 waitflag 的默認值是一個非零整數,表示若是瑣已經被其餘線程佔用,那麼當前線程將一直等待,只到其餘線程釋放,而後獲取訪瑣。若是將參數 waitflag 置爲 0 ,那麼當前線程會嘗試獲取瑣,無論瑣是否被其餘線程佔用,當前線程都不會等待。線程

lock.release ()

釋放所佔用的瑣。

lock.locked ()

判斷瑣是否被佔用。

如今咱們回過頭來看文章開始處給出的那段代碼:代碼中定義了一個函數 threadTest ,它將全局變量逐一的增長 10000 ,而後在主線程中開啓了 10 個子線程來調用 threadTest 函數。但結果並非預料中的 10000 * 10 ,緣由主要是對 count 的併發操做引來的。全局變量 count 是共享資源,對它的操做應該串行的進行。下面對那段代碼進行修改,在對 count 操做的時候,進行加瑣處理。看看程序運行的結果是否和預期一致。修改後的代碼:

#coding=gbk
import thread, time, random
count = 0
lock = thread.allocate_lock() #建立一個瑣對象
def threadTest():
    global count, lock
    lock.acquire() #獲取瑣

    for i in xrange(10000):
        count += 1

    lock.release() #釋放瑣
for i in xrange(10):
    thread.start_new_thread(threadTest, ())
time.sleep(3)
print count

 

threading.Thread

threading經過對thread模塊進行二次封裝,提供了更方便的API來操做線程。

Thread 是threading模塊中最重要的類之一,可使用它來建立線程。有兩種方式來建立線程:一種是經過繼承Thread類,重寫它的run方法;另外一種是建立一個threading.Thread對象,在它的初始化函數(__init__)中將可調用對象做爲參數傳入。下面分別舉例說明。先來看看經過繼承threading.Thread類來建立線程的例子:

 1 #coding=gbk
 2 import threading, time, random
 3 count = 0
 4 class Counter(threading.Thread):
 5     def __init__(self, lock, threadName):
 6         '''@summary: 初始化對象。
 7         
 8         @param lock: 瑣對象。
 9         @param threadName: 線程名稱。
10         '''
11         super(Counter, self).__init__(name = threadName)  #注意:必定要顯式的調用父類的初始
12 化函數。
13         self.lock = lock
14     
15     def run(self):
16         '''@summary: 重寫父類run方法,在線程啓動後執行該方法內的代碼。
17         '''
18         global count
19         self.lock.acquire()
20         for i in xrange(10000):
21             count = count + 1
22         self.lock.release()
23 lock = threading.Lock()
24 for i in range(5): 
25     Counter(lock, "thread-" + str(i)).start()
26 time.sleep(2)    #確保線程都執行完畢
27 print count

 

在代碼中,咱們建立了一個Counter類,它繼承了threading.Thread。初始化函數接收兩個參數,一個是瑣對象,另外一個是線程的名稱。在Counter中,重寫了從父類繼承的run方法,run方法將一個全局變量逐一的增長10000。在接下來的代碼中,建立了五個Counter對象,分別調用其start方法。最後打印結果。這裏要說明一下run方法 和start方法: 它們都是從Thread繼承而來的,run()方法將在線程開啓後執行,能夠把相關的邏輯寫到run方法中(一般把run方法稱爲活動[Activity]。);start()方法用於啓動線程。

 

再看看另一種建立線程的方法:

 1 import threading, time, random
 2 count = 0
 3 lock = threading.Lock()
 4 def doAdd():
 5     '''@summary: 將全局變量count 逐一的增長10000。
 6     '''
 7     global count, lock
 8     lock.acquire()
 9     for i in xrange(10000):
10         count = count + 1
11     lock.release()
12 for i in range(5):
13     threading.Thread(target = doAdd, args = (), name = 'thread-' + str(i)).start()
14 time.sleep(2)    #確保線程都執行完畢
15 print count

在這段代碼中,咱們定義了方法doAdd,它將全局變量count 逐一的增長10000。而後建立了5個Thread對象,把函數對象doAdd 做爲參數傳給它的初始化函數,再調用Thread對象的start方法,線程啓動後將執行doAdd函數。這裏有必要介紹一下threading.Thread類的初始化函數原型:

def __init__(self, group=None, target=None, name=None, args=(), kwargs={})

  •   參數group是預留的,用於未來擴展;
  •   參數target是一個可調用對象(也稱爲活動[activity]),在線程啓動後執行;
  •   參數name是線程的名字。默認值爲「Thread-N「,N是一個數字。
  •   參數args和kwargs分別表示調用target時的參數列表和關鍵字參數。

Thread類還定義瞭如下經常使用方法與屬性:

Thread.getName()

Thread.setName()

Thread.name

用於獲取和設置線程的名稱。

Thread.ident

獲取線程的標識符。線程標識符是一個非零整數,只有在調用了start()方法以後該屬性纔有效,不然它只返回None。

Thread.is_alive()

Thread.isAlive()

判斷線程是不是激活的(alive)。從調用start()方法啓動線程,到run()方法執行完畢或遇到未處理異常而中斷 這段時間內,線程是激活的。

Thread.join([timeout])

調用Thread.join將會使主調線程堵塞,直到被調用線程運行結束或超時。參數timeout是一個數值類型,表示超時時間,若是未提供該參數,那麼主調線程將一直堵塞到被調線程結束。下面舉個例子說明join()的使用:

 1 import threading, time
 2 def doWaiting():
 3     print 'start waiting:', time.strftime('%H:%M:%S')
 4     time.sleep(3)
 5     print 'stop waiting', time.strftime('%H:%M:%S')
 6 thread1 = threading.Thread(target = doWaiting)
 7 thread1.start()
 8 time.sleep(1)  #確保線程thread1已經啓動
 9 print 'start join'
10 thread1.join()    #將一直堵塞,直到thread1運行結束。
11 print 'end join'

threading.RLock和threading.Lock

在threading模塊中,定義兩種類型的瑣:threading.Lock和threading.RLock。它們之間有一點細微的區別,經過比較下面兩段代碼來講明:

1 import threading
2 lock = threading.Lock()    #Lock對象
3 lock.acquire()
4 lock.acquire()  #產生了死瑣。
5 lock.release()
6 lock.release()
1 import threading
2 rLock = threading.RLock()  #RLock對象
3 rLock.acquire()
4 rLock.acquire()    #在同一線程內,程序不會堵塞。
5 rLock.release()
6 rLock.release()

這兩種瑣的主要區別是:RLock容許在同一線程中被屢次acquire。而Lock卻不容許這種狀況。注意:若是使用RLock,那麼acquire和release必須成對出現,即調用了n次acquire,必須調用n次的release才能真正釋放所佔用的瑣。

threading.Condition

能夠把Condiftion理解爲一把高級的瑣,它提供了比Lock, RLock更高級的功能,容許咱們可以控制複雜的線程同步問題。threadiong.Condition在內部維護一個瑣對象(默認是RLock),能夠在建立Condigtion對象的時候把瑣對象做爲參數傳入。Condition也提供了acquire, release方法,其含義與瑣的acquire, release方法一致,其實它只是簡單的調用內部瑣對象的對應的方法而已。Condition還提供了以下方法(特別要注意:這些方法只有在佔用瑣(acquire)以後才能調用,不然將會報RuntimeError異常。):

Condition.wait([timeout]):

wait方法釋放內部所佔用的瑣,同時線程被掛起,直至接收到通知被喚醒或超時(若是提供了timeout參數的話)。當線程被喚醒並從新佔有瑣的時候,程序纔會繼續執行下去。

Condition.notify():

喚醒一個掛起的線程(若是存在掛起的線程)。注意:notify()方法不會釋放所佔用的瑣。

Condition.notify_all()

Condition.notifyAll()

喚醒全部掛起的線程(若是存在掛起的線程)。注意:這些方法不會釋放所佔用的瑣。

 

threading.Event

Event實現與Condition相似的功能,不過比Condition簡單一點。它經過維護內部的標識符來實現線程間的同步問題。(threading.Event和.NET中的System.Threading.ManualResetEvent類實現一樣的功能。)

Event.wait([timeout])

堵塞線程,直到Event對象內部標識位被設爲True或超時(若是提供了參數timeout)。

Event.set()

將標識位設爲Ture

Event.clear()

將標識伴設爲False。

Event.isSet()

判斷標識位是否爲Ture。

 

threading.Timer

threading.Timer是threading.Thread的子類,能夠在指定時間間隔後執行某個操做。下面是Python手冊上提供的一個例子:

1 def hello():
2     print "hello, world"
3 t = Timer(3, hello)
4 t.start() # 3秒鐘以後執行hello函數。

threading模塊中還有一些經常使用的方法沒有介紹:

threading.active_count()
threading.activeCount()

獲取當前活動的(alive)線程的個數。

threading.current_thread()
threading.currentThread()

獲取當前的線程對象(Thread object)。

threading.enumerate()

獲取當前全部活動線程的列表。

threading.settrace(func)

設置一個跟蹤函數,用於在run()執行以前被調用。

threading.setprofile(func)

設置一個跟蹤函數,用於在run()執行完畢以後調用。

相關文章
相關標籤/搜索