Python標準庫08 多線程與同步 (threading包)Linux多線程與同步Linux多線程與同步Python動態類型Python物件導向的基本概念Python物件導向的進一步拓展Linux多

來源:互聯網
上載者:User

作者:Vamei 出處:http://www.cnblogs.com/vamei 歡迎轉載,也請保留這段聲明。謝謝!

 

Python主要通過標準庫中的threading包來實現多線程。在當今網路時代,每個伺服器都會接收到大量的請求。伺服器可以利用多線程的方式來處理這些請求,以提高對網路連接埠的讀寫效率。Python是一種網路伺服器的後台工作語言 (比如豆瓣網),所以多線程也就很自然被Python語言支援。

(關於多線程的原理和C實現方法,請參考我之前寫的Linux多線程與同步,要瞭解race condition, mutex和condition variable的概念)

 

1. 多線程售票以及同步

我們使用Python來實現Linux多線程與同步文中的售票程式。我們使用mutex (也就是Python中的Lock類對象) 來實現線程的同步:

# A program to simulate selling tickets in multi-thread way# Written by Vameiimport threadingimport timeimport os# This function could be any function to do other chores.def doChore():    time.sleep(0.5)# Function for each threaddef booth(tid):    global i    global lock
while True: lock.acquire()               # Lock; or wait if other thread is holding the lock if i != 0: i = i - 1                 # Sell tickets
print(tid,':now left:',i) # Tickets left doChore()                # Other critical operations else: print("Thread_id",tid," No more tickets") os._exit(0) # Exit the whole process immediately lock.release()               # Unblock doChore()                    # Non-critical operations# Start of the main functioni = 100 # Available ticket number lock = threading.Lock() # Lock (i.e., mutex)# Start 10 threadsfor k in range(10): new_thread = threading.Thread(target=booth,args=(k,))   # Set up thread; target: the callable (function) to be run, args: the argument for the callable new_thread.start()                         # run the thread

我們使用了兩個全域變數,一個是i,用以儲存剩餘票數;一個是lock對象,用於同步線程對i的修改。此外,在最後的for迴圈中,我們總共設定了10個線程。每個線程都執行booth()函數。線程在調用start()方法的時候正式啟動 (實際上,電腦中最多會有11個線程,因為主程式本身也會佔用一個線程)。Python使用threading.Thread對象來代表線程,用threading.Lock對象來代表一個互斥鎖 (mutex)。

有兩點需要注意:

  • 我們在函數中使用global來聲明變數為全域變數,從而讓多線程共用i和lock (在C語言中,我們通過將變數放在所有函數外面來讓它成為全域變數)。如果不這麼聲明,由於i和lock是不可變資料對象,它們將被當作一個局部變數(參看Python動態類型)。如果是可變資料對象的話,則不需要global聲明。我們甚至可以將可變資料對象作為參數來傳遞給線程函數。這些線程將共用這些可變資料對象。
  • 我們在booth中使用了兩個doChore()函數。可以在未來改進程式,以便讓線程除了進行i=i-1之外,做更多的操作,比如列印剩餘票數,找錢,或者喝口水之類的。第一個doChore()依然在Lock內部,所以可以安全地使用共用資源 (critical operations, 比如列印剩餘票數)。第二個doChore()時,Lock已經被釋放,所以不能再去使用共用資源。這時候可以做一些不使用共用資源的操作 (non-critical operation, 比如找錢、喝水)。我故意讓doChore()等待了0.5秒,以代表這些額外的操作可能花費的時間。你可以定義的函數來代替doChore()。

 

2. OOP建立線程

上面的Python程式非常類似於一個面向過程的C程式。我們下面介紹如何通過物件導向 (OOP, object-oriented programming,參看Python物件導向的基本概念和Python物件導向的進一步拓展) 的方法實現多線程,其核心是繼承threading.Thread類。我們上面的for迴圈中已經利用了threading.Thread()的方法來建立一個Thread對象,並將函數booth()以及其參數傳遞給改對象,並調用start()方法來運行線程。OOP的話,通過修改Thread類的run()方法來定義線程所要執行的命令。

# A program to simulate selling tickets in multi-thread way# Written by Vameiimport threadingimport timeimport os# This function could be any function to do other chores.def doChore():    time.sleep(0.5)# Function for each threadclass BoothThread(threading.Thread):    def __init__(self, tid, monitor):        self.tid          = tid        self.monitor = monitor
        threading.Thread.__init__(self) def run(self): while True:  monitor['lock'].acquire()       # Lock; or wait if other thread is holding the lock     if monitor['tick'] != 0: monitor['tick'] = monitor['tick'] - 1 # Sell tickets
print(self.tid,':now left:',monitor['tick']) # Tickets left doChore()                   # Other critical operations else: print("Thread_id",self.tid," No more tickets") os._exit(0)      # Exit the whole process immediately monitor['lock'].release()      # Unblock doChore()      # Non-critical operations# Start of the main functionmonitor = {'tick':100, 'lock':threading.Lock()}# Start 10 threadsfor k in range(10): new_thread = BoothThread(k, monitor) new_thread.start()

我們自己定義了一個類BoothThread, 這個類繼承自thread.Threading類。然後我們把上面的booth()所進行的操作統統放入到BoothThread類的run()方法中。注意,我們沒有使用全域變數聲明global,而是使用了一個詞典monitor存放全域變數,然後把詞典作為參數傳遞給線程函數。由於詞典是可變資料對象,所以當它被傳遞給函數的時候,函數所使用的依然是同一個對象,相當於被多個線程所共用。這也是多線程乃至於多進程編程的一個技巧 (應盡量避免上面的global聲明的用法,因為它並不適用於windows平台)。

上面OOP編程方法與面向過程的編程方法相比,並沒有帶來太大實質性的差別。

 

3. 其他:

threading.Thread對象: 我們已經介紹了該對象的start()和run(), 此外:

  • join()方法,調用該方法的線程將等待直到改Thread對象完成,再恢複運行。這與進程間調用wait()函數相類似。

 

下面的對象用於處理多線程同步。對象一旦被建立,可以被多個線程共用,並根據情況阻塞某些進程。請與Linux多線程與同步中的同步工具參照閱讀。

threading.Lock對象: mutex, 有acquire()和release()方法。

threading.Condition對象: condition variable,建立該對象時,會包含一個Lock對象 (因為condition variable總是和mutex一起使用)。可以對Condition對象調用acquire()和release()方法,以控制潛在的Lock對象。此外:

  • wait()方法,相當於cond_wait()
  • notify_all(),相當與cond_broadcast()
  • nofify(),與notify_all()功能類似,但只喚醒一個等待的線程,而不是全部
threading.Semaphore對象: semaphore,也就是計數鎖(semaphore傳統意義上是一種進程間同步工具,見Linux處理序間通訊)。建立對象的時候,可以傳遞一個整數作為計數上限 (sema = threading.Semaphore(5))。它與Lock類似,也有Lock的兩個方法。
threading.Event對象: 與threading.Condition相類似,相當於沒有潛在的Lock保護的condition variable。對象有True和False兩個狀態。可以多個線程使用wait()等待,直到某個線程調用該對象的set()方法,將對象設定為True。線程可以調用對象的clear()方法來重設對象為False狀態。
 
 
4. 練習:
參照Linux多線程與同步中的condition variable的例子,使用Python實現。同時考慮使用面向過程和物件導向的編程方法。
更多的threading的內容請參考:

http://docs.python.org/library/threading.html

 

總結:

threading.Thread

Lock, Condition, Semaphore, Event

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.