這段時間一直在用 Python 寫一個遊戲的伺服器程式。在編寫過程中,不可避免的要用多執行緒來處理與客戶端的互動。 Python 標準庫提供了 thread 和 threading 兩個模組來對多執行緒進行支援。其中, thread 模組以低階、原始的方式來處理和控制執行緒,而 threading 模組通過對 thread 進行二次封裝,提供了更方便的 api 來處理執行緒。 雖然使用 thread 沒有 threading 來的方便,但它更靈活。今天先介紹 thread 模組的基本使用,下一篇 將介紹threading 模組。
在介紹 thread 之前,先看一段程式碼,猜猜程式執行完成之後,在控制檯上輸出的結果是什麼?
1 2 3 4 5 6 7 8 9 10 11 |
#coding=gbk import thread, time, random count = 0 def threadTest(): global count for i in xrange(10000): count += 1 for i in range(10): thread.start_new_thread(threadTest, ()) #如果對start_new_thread函式不是很瞭解,不要著急,馬上就會講解 time.sleep(3) print count #count是多少呢?是10000 * 10 嗎? |
thread.start_new_thread ( function , args [ , kwargs ] )
函式將建立一個新的執行緒,並返回該執行緒的識別符號(識別符號為整數)。引數 function 表示執行緒建立之後,立即執行的函式,引數 args 是該函式的引數,它是一個元組型別;第二個引數 kwargs 是可選的,它為函式提供了命名引數字典。函式執行完畢之後,執行緒將自動退出。如果函式在執行過程中遇到未處理的異常,該執行緒將退出,但不會影響其他執行緒的執行。 下面是一個簡單的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#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 異常,如果沒有處理該異常,執行緒將結束。
thread.get_ident ()
返回當前執行緒的識別符號,識別符號是一個非零整數。
thread.interrupt_main ()
在主執行緒中觸發 KeyboardInterrupt 異常。子執行緒可以使用該方法來中斷主執行緒。下面的例子演示了在子執行緒中呼叫 interrupt_main ,在主執行緒中捕獲異常 :
1 2 3 4 5 6 7 |
import thread, time thread.start_new_thread(lambda : (thread.interrupt_main(), ), ()) try: time.sleep(2) except KeyboardInterrupt, e: print 'error:', e print 'over' |
下面介紹 thread 模組中的瑣,瑣可以保證在任何時刻,最多隻有一個執行緒可以訪問共享資源。
thread.LockType 是 thread 模組中定義的瑣型別。它有如下方法:
lock.acquire ( [ waitflag ] )
獲取瑣。函式返回一個布林值,如果獲取成功,返回 True ,否則返回 False 。引數 waitflag 的預設值是一個非零整數,表示如果瑣已經被其他執行緒佔用,那麼當前執行緒將一直等待,只到其他執行緒釋放,然後獲取訪瑣。如果將引數 waitflag 置為 0 ,那麼當前執行緒會嘗試獲取瑣,不管瑣是否被其他執行緒佔用,當前執行緒都不會等待。
lock.release ()
釋放所佔用的瑣。
lock.locked ()
判斷瑣是否被佔用。
現在我們回過頭來看文章開始處給出的那段程式碼:程式碼中定義了一個函式 threadTest ,它將全域性變數逐一的增加 10000 ,然後在主執行緒中開啟了 10 個子執行緒來呼叫 threadTest 函式。但結果並不是預料中的 10000 * 10 ,原因主要是對 count 的併發操作引來的。全域性變數 count 是共享資源,對它的操作應該序列的進行。下面對那段程式碼進行修改,在對 count 操作的時候,進行加瑣處理。看看程式執行的結果是否和預期一致。修改後的程式碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#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 |
thread模組是不是並沒有想像中的那麼難!簡單就是美,這就是Python。更多關於thread模組的內容,請參考Python手冊 thread 模組