萬盛學電腦網

 萬盛學電腦網 >> 網絡編程 >> 編程語言綜合 >> Python線程詳解

Python線程詳解

   這篇文章主要介紹了Python線程詳解,本文詳細講解了線程方方面面的知識,如線程基礎知識線程狀態、線程同步(鎖)、線程通信(條件變量)等內容,需要的朋友可以參考下

  1. 線程基礎

  1.1. 線程狀態

  線程有5種狀態,狀態轉換的過程如下圖所示:

  1.2. 線程同步(鎖)

  多線程的優勢在於可以同時運行多個任務(至少感覺起來是這樣)。但是當線程需要共享數據時,可能存在數據不同步的問題。考慮這樣一種情況:一個列表裡所有元素都是0,線程"set"從後向前把所有元素改成1,而線程"print"負責從前往後讀取列表並打印。那麼,可能線程"set"開始改的時候,線程"print"便來打印列表了,輸出就成了一半0一半1,這就是數據的不同步。為了避免這種情況,引入了鎖的概念。

  鎖有兩種狀態——鎖定和未鎖定。每當一個線程比如"set"要訪問共享數據時,必須先獲得鎖定;如果已經有別的線程比如"print"獲得鎖定了,那麼就讓線程"set"暫停,也就是同步阻塞;等到線程"print"訪問完畢,釋放鎖以後,再讓線程"set"繼續。經過這樣的處理,打印列表時要麼全部輸出0,要麼全部輸出1,不會再出現一半0一半1的尴尬場面。

  線程與鎖的交互如下圖所示:

  1.3. 線程通信(條件變量)

  然而還有另外一種尴尬的情況:列表並不是一開始就有的;而是通過線程"create"創建的。如果"set"或者"print" 在"create"還沒有運行的時候就訪問列表,將會出現一個異常。使用鎖可以解決這個問題,但是"set"和"print"將需要一個無限循環——他們不知道"create"什麼時候會運行,讓"create"在運行後通知"set"和"print"顯然是一個更好的解決方案。於是,引入了條件變量。

  條件變量允許線程比如"set"和"print"在條件不滿足的時候(列表為None時)等待,等到條件滿足的時候(列表已經創建)發出一個通知,告訴"set" 和"print"條件已經有了,你們該起床干活了;然後"set"和"print"才繼續運行。

  線程與條件變量的交互如下圖所示:

  1.4. 線程運行和阻塞的狀態轉換

  最後看看線程運行和阻塞狀態的轉換。

  阻塞有三種情況:

  同步阻塞是指處於競爭鎖定的狀態,線程請求鎖定時將進入這個狀態,一旦成功獲得鎖定又恢復到運行狀態;

  等待阻塞是指等待其他線程通知的狀態,線程獲得條件鎖定後,調用“等待”將進入這個狀態,一旦其他線程發出通知,線程將進入同步阻塞狀態,再次競爭條件鎖定;

  而其他阻塞是指調用time.sleep()、anotherthread.join()或等待IO時的阻塞,這個狀態下線程不會釋放已獲得的鎖定。

  tips: 如果能理解這些內容,接下來的主題將是非常輕松的;並且,這些內容在大部分流行的編程語言裡都是一樣的。(意思就是非看懂不可 >_< 嫌作者水平低找別人的教程也要看懂)

  2. thread

  Python通過兩個標准庫thread和threading提供對線程的支持。thread提供了低級別的、原始的線程以及一個簡單的鎖。

  代碼如下:

  # encoding: UTF-8

  import thread

  import time

  # 一個用於在線程中執行的函數

  def func():

  for i in range(5):

  print 'func'

  time.sleep(1)

  # 結束當前線程

  # 這個方法與thread.exit_thread()等價

  thread.exit() # 當func返回時,線程同樣會結束

  # 啟動一個線程,線程立即開始運行

  # 這個方法與thread.start_new_thread()等價

  # 第一個參數是方法,第二個參數是方法的參數

  thread.start_new(func, ()) # 方法沒有參數時需要傳入空tuple

  # 創建一個鎖(LockType,不能直接實例化)

  # 這個方法與thread.allocate_lock()等價

  lock = thread.allocate()

  # 判斷鎖是鎖定狀態還是釋放狀態

  print lock.locked()

  # 鎖通常用於控制對共享資源的訪問

  count = 0

  # 獲得鎖,成功獲得鎖定後返回True

  # 可選的timeout參數不填時將一直阻塞直到獲得鎖定

  # 否則超時後將返回False

  if lock.acquire():

  count += 1

  # 釋放鎖

  lock.release()

  # thread模塊提供的線程都將在主線程結束後同時結束

  time.sleep(6)

  thread 模塊提供的其他方法:

  thread.interrupt_main(): 在其他線程中終止主線程。

  thread.get_ident(): 獲得一個代表當前線程的魔法數字,常用於從一個字典中獲得線程相關的數據。這個數字本身沒有任何含義,並且當線程結束後會被新線程復用。

  thread還提供了一個ThreadLocal類用於管理線程相關的數據,名為 thread._local,threading中引用了這個類。

  由於thread提供的線程功能不多,無法在主線程結束後繼續運行,不提供條件變量等等原因,一般不使用thread模塊,這裡就不多介紹了。

  3. threading

  threading基於Java的線程模型設計。鎖(Lock)和條件變量(Condition)在Java中是對象的基本行為(每一個對象都自帶了鎖和條件變量),而在Python中則是獨立的對象。Python Thread提供了Java Thread的行為的子集;沒有優先級、線程組,線程也不能被停止、暫停、恢復、中斷。Java Thread中的部分被Python實現了的靜態方法在threading中以模塊方法的形式提供。

  threading 模塊提供的常用方法:

  threading.currentThread(): 返回當前的線程變量。

  threading.enumerate(): 返回一個包含正在運行的線程的list。正在運行指線程啟動後、結束前,不包括啟動前和終止後的線程。

  threading.activeCount(): 返回正在運行的線程數量,與len(threading.enumerate())有相同的結果。

  threading模塊提供的類:

  Thread, Lock, Rlock, Condition, [Bounded]Semaphore, Event, Timer, local.

  3.1. Thread

  Thread是線程類,與Java類似,有兩種使用方法,直接傳入要運行的方法或從Thread繼承並覆蓋run():

   代碼如下:

  # encoding: UTF-8

  import threading

  # 方法1:將要執行的方法作為參數傳給Thread的構造方法

  def func():

  print 'func() passed to Thread'

  t = threading.Thread(target=func)

  t.start()

  # 方法2:從Thread繼承,並重寫run()

  class MyThread(threading.Thread):

  def run(self):

  print 'MyThread extended from Thread'

  t = MyThread()

  t.start()

  構造方法:

  Thread(group=None, target=None, name=None, args=(), kwargs={})

  group: 線程組,目前還沒有實現,庫引用中提示必須是None;

  target: 要執行的方法;

  name: 線程名;

  args/kwargs: 要傳入方法的參數。

  實例方法:

  isAlive(): 返回線程是否在運行。正在運行指啟動後、終止前。

  get/setName(name): 獲取/設置線程名。

  is/setDaemon(bool): 獲取/設置是否守護線程。初始值從創建該線程的線程繼承。當沒有非守護線程仍在運行時,程序將終止。

  start(): 啟動線程。

  join([timeout]): 阻塞當前上下文環境的線程,直到調用此方法的線程終止或到達指定的timeout(可選參數)。

  一個使用join()的例子:

  代碼如下:

  # encoding: UTF-8

  import threading

  import time

  def context(tJoin):

  print 'in threadContext.'

  tJoin.start()

  # 將阻塞tContext直到threadJoin終止。

  tJoin.join()

  # tJoin終止後繼續執行。

  print 'out threadContext.'

  def join():

  print 'in threadJoin.'

  time.sleep(1)

  print 'out threadJoin.'

  tJoin = threading.Thread(target=join)

  tContext = threading.Thread(target=context, args=(tJoin,))

  tContext.start()

  運行結果:

copyright © 萬盛學電腦網 all rights reserved