亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

Python線程詳解

系統 1575 0

1. 線程基礎

1.1. 線程狀態

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

Python線程詳解_第1張圖片

1.2. 線程同步(鎖)

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

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

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

Python線程詳解_第2張圖片

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

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

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

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

Python線程詳解_第3張圖片

Python線程詳解_第4張圖片

1.4. 線程運行和阻塞的狀態轉換
最后看看線程運行和阻塞狀態的轉換。

Python線程詳解_第5張圖片

阻塞有三種情況:

同步阻塞是指處于競爭鎖定的狀態,線程請求鎖定時將進入這個狀態,一旦成功獲得鎖定又恢復到運行狀態;
等待阻塞是指等待其他線程通知的狀態,線程獲得條件鎖定后,調用“等待”將進入這個狀態,一旦其他線程發出通知,線程將進入同步阻塞狀態,再次競爭條件鎖定;
而其他阻塞是指調用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()

運行結果:

復制代碼 代碼如下:

in threadContext.
in threadJoin.
out threadJoin.
out threadContext.

3.2. Lock

Lock(指令鎖)是可用的最低級的同步指令。Lock處于鎖定狀態時,不被特定的線程擁有。Lock包含兩種狀態――鎖定和非鎖定,以及兩個基本的方法。

可以認為Lock有一個鎖定池,當線程請求鎖定時,將線程至于池中,直到獲得鎖定后出池。池中的線程處于狀態圖中的同步阻塞狀態。

構造方法:

Lock()

實例方法:

acquire([timeout]): 使線程進入同步阻塞狀態,嘗試獲得鎖定。
release(): 釋放鎖。使用前線程必須已獲得鎖定,否則將拋出異常。

復制代碼 代碼如下:

# encoding: UTF-8
import threading
import time
?
data = 0
lock = threading.Lock()
?
def func():
??? global data
??? print '%s acquire lock...' % threading.currentThread().getName()
???
??? # 調用acquire([timeout])時,線程將一直阻塞,
??? # 直到獲得鎖定或者直到timeout秒后(timeout參數可選)。
??? # 返回是否獲得鎖。
??? if lock.acquire():
??????? print '%s get the lock.' % threading.currentThread().getName()
??????? data += 1
??????? time.sleep(2)
??????? print '%s release lock...' % threading.currentThread().getName()
???????
??????? # 調用release()將釋放鎖。
??????? lock.release()
?
t1 = threading.Thread(target=func)
t2 = threading.Thread(target=func)
t3 = threading.Thread(target=func)
t1.start()
t2.start()
t3.start()

3.3. RLock

RLock(可重入鎖)是一個可以被同一個線程請求多次的同步指令。RLock使用了“擁有的線程”和“遞歸等級”的概念,處于鎖定狀態時,RLock被某個線程擁有。擁有RLock的線程可以再次調用acquire(),釋放鎖時需要調用release()相同次數。

可以認為RLock包含一個鎖定池和一個初始值為0的計數器,每次成功調用 acquire()/release(),計數器將+1/-1,為0時鎖處于未鎖定狀態。

構造方法:

RLock()

實例方法:
acquire([timeout])/release(): 跟Lock差不多。

復制代碼 代碼如下:

# encoding: UTF-8
import threading
import time
?
rlock = threading.RLock()
?
def func():
??? # 第一次請求鎖定
??? print '%s acquire lock...' % threading.currentThread().getName()
??? if rlock.acquire():
??????? print '%s get the lock.' % threading.currentThread().getName()
??????? time.sleep(2)
???????
??????? # 第二次請求鎖定
??????? print '%s acquire lock again...' % threading.currentThread().getName()
??????? if rlock.acquire():
??????????? print '%s get the lock.' % threading.currentThread().getName()
??????????? time.sleep(2)
???????
??????? # 第一次釋放鎖
??????? print '%s release lock...' % threading.currentThread().getName()
??????? rlock.release()
??????? time.sleep(2)
???????
??????? # 第二次釋放鎖
??????? print '%s release lock...' % threading.currentThread().getName()
??????? rlock.release()
?
t1 = threading.Thread(target=func)
t2 = threading.Thread(target=func)
t3 = threading.Thread(target=func)
t1.start()
t2.start()
t3.start()

3.4. Condition

Condition(條件變量)通常與一個鎖關聯。需要在多個Contidion中共享一個鎖時,可以傳遞一個Lock/RLock實例給構造方法,否則它將自己生成一個RLock實例。

可以認為,除了Lock帶有的鎖定池外,Condition還包含一個等待池,池中的線程處于狀態圖中的等待阻塞狀態,直到另一個線程調用notify()/notifyAll()通知;得到通知后線程進入鎖定池等待鎖定。

構造方法:

Condition([lock/rlock])

實例方法:

acquire([timeout])/release(): 調用關聯的鎖的相應方法。
wait([timeout]): 調用這個方法將使線程進入Condition的等待池等待通知,并釋放鎖。使用前線程必須已獲得鎖定,否則將拋出異常。
notify(): 調用這個方法將從等待池挑選一個線程并通知,收到通知的線程將自動調用acquire()嘗試獲得鎖定(進入鎖定池);其他線程仍然在等待池中。調用這個方法不會釋放鎖定。使用前線程必須已獲得鎖定,否則將拋出異常。
notifyAll(): 調用這個方法將通知等待池中所有的線程,這些線程都將進入鎖定池嘗試獲得鎖定。調用這個方法不會釋放鎖定。使用前線程必須已獲得鎖定,否則將拋出異常。

例子是很常見的生產者/消費者模式:

復制代碼 代碼如下:

# encoding: UTF-8
import threading
import time
?
# 商品
product = None
# 條件變量
con = threading.Condition()
?
# 生產者方法
def produce():
??? global product
???
??? if con.acquire():
??????? while True:
??????????? if product is None:
??????????????? print 'produce...'
??????????????? product = 'anything'
???????????????
??????????????? # 通知消費者,商品已經生產
??????????????? con.notify()
???????????
??????????? # 等待通知
??????????? con.wait()
??????????? time.sleep(2)
?
# 消費者方法
def consume():
??? global product
???
??? if con.acquire():
??????? while True:
??????????? if product is not None:
??????????????? print 'consume...'
??????????????? product = None
???????????????
??????????????? # 通知生產者,商品已經沒了
??????????????? con.notify()
???????????
??????????? # 等待通知
??????????? con.wait()
??????????? time.sleep(2)
?
t1 = threading.Thread(target=produce)
t2 = threading.Thread(target=consume)
t2.start()
t1.start()

3.5. Semaphore/BoundedSemaphore

Semaphore(信號量)是計算機科學史上最古老的同步指令之一。Semaphore管理一個內置的計數器,每當調用acquire()時-1,調用release() 時+1。計數器不能小于0;當計數器為0時,acquire()將阻塞線程至同步鎖定狀態,直到其他線程調用release()。

基于這個特點,Semaphore經常用來同步一些有“訪客上限”的對象,比如連接池。

BoundedSemaphore 與Semaphore的唯一區別在于前者將在調用release()時檢查計數器的值是否超過了計數器的初始值,如果超過了將拋出一個異常。

構造方法:
Semaphore(value=1): value是計數器的初始值。

實例方法:
acquire([timeout]): 請求Semaphore。如果計數器為0,將阻塞線程至同步阻塞狀態;否則將計數器-1并立即返回。
release(): 釋放Semaphore,將計數器+1,如果使用BoundedSemaphore,還將進行釋放次數檢查。release()方法不檢查線程是否已獲得 Semaphore。

復制代碼 代碼如下:

# encoding: UTF-8
import threading
import time
?
# 計數器初值為2
semaphore = threading.Semaphore(2)
?
def func():
???
??? # 請求Semaphore,成功后計數器-1;計數器為0時阻塞
??? print '%s acquire semaphore...' % threading.currentThread().getName()
??? if semaphore.acquire():
???????
??????? print '%s get semaphore' % threading.currentThread().getName()
??????? time.sleep(4)
???????
??????? # 釋放Semaphore,計數器+1
??????? print '%s release semaphore' % threading.currentThread().getName()
??????? semaphore.release()
?
t1 = threading.Thread(target=func)
t2 = threading.Thread(target=func)
t3 = threading.Thread(target=func)
t4 = threading.Thread(target=func)
t1.start()
t2.start()
t3.start()
t4.start()
?
time.sleep(2)
?
# 沒有獲得semaphore的主線程也可以調用release
# 若使用BoundedSemaphore,t4釋放semaphore時將拋出異常
print 'MainThread release semaphore without acquire'
semaphore.release()

3.6. Event

Event(事件)是最簡單的線程通信機制之一:一個線程通知事件,其他線程等待事件。Event內置了一個初始為False的標志,當調用set()時設為True,調用clear()時重置為 False。wait()將阻塞線程至等待阻塞狀態。

Event其實就是一個簡化版的 Condition。Event沒有鎖,無法使線程進入同步阻塞狀態。

構造方法:

Event()

實例方法:
isSet(): 當內置標志為True時返回True。
set(): 將標志設為True,并通知所有處于等待阻塞狀態的線程恢復運行狀態。
clear(): 將標志設為False。
wait([timeout]): 如果標志為True將立即返回,否則阻塞線程至等待阻塞狀態,等待其他線程調用set()。

復制代碼 代碼如下:

# encoding: UTF-8
import threading
import time
?
event = threading.Event()
?
def func():
??? # 等待事件,進入等待阻塞狀態
??? print '%s wait for event...' % threading.currentThread().getName()
??? event.wait()
???
??? # 收到事件后進入運行狀態
??? print '%s recv event.' % threading.currentThread().getName()
?
t1 = threading.Thread(target=func)
t2 = threading.Thread(target=func)
t1.start()
t2.start()
?
time.sleep(2)
?
# 發送事件通知
print 'MainThread set event.'
event.set()

3.7. Timer
Timer(定時器)是Thread的派生類,用于在指定時間后調用一個方法。

構造方法:
Timer(interval, function, args=[], kwargs={})
interval: 指定的時間
function: 要執行的方法
args/kwargs: 方法的參數

實例方法:
Timer從Thread派生,沒有增加實例方法。

復制代碼 代碼如下:

# encoding: UTF-8
import threading
?
def func():
??? print 'hello timer!'
?
timer = threading.Timer(5, func)
timer.start()

3.8. local

local是一個小寫字母開頭的類,用于管理 thread-local(線程局部的)數據。對于同一個local,線程無法訪問其他線程設置的屬性;線程設置的屬性不會被其他線程設置的同名屬性替換。

可以把local看成是一個“線程-屬性字典”的字典,local封裝了從自身使用線程作為 key檢索對應的屬性字典、再使用屬性名作為key檢索屬性值的細節。

復制代碼 代碼如下:

# encoding: UTF-8
import threading
?
local = threading.local()
local.tname = 'main'
?
def func():
??? local.tname = 'notmain'
??? print local.tname
?
t1 = threading.Thread(target=func)
t1.start()
t1.join()
?
print local.tname

熟練掌握Thread、Lock、Condition就可以應對絕大多數需要使用線程的場合,某些情況下local也是非常有用的東西。本文的最后使用這幾個類展示線程基礎中提到的場景:

復制代碼 代碼如下:

# encoding: UTF-8
import threading
?
alist = None
condition = threading.Condition()
?
def doSet():
??? if condition.acquire():
??????? while alist is None:
??????????? condition.wait()
??????? for i in range(len(alist))[::-1]:
??????????? alist[i] = 1
??????? condition.release()
?
def doPrint():
??? if condition.acquire():
??????? while alist is None:
??????????? condition.wait()
??????? for i in alist:
??????????? print i,
??????? print
??????? condition.release()
?
def doCreate():
??? global alist
??? if condition.acquire():
??????? if alist is None:
??????????? alist = [0 for i in range(10)]
??????????? condition.notifyAll()
??????? condition.release()
?
tset = threading.Thread(target=doSet,name='tset')
tprint = threading.Thread(target=doPrint,name='tprint')
tcreate = threading.Thread(target=doCreate,name='tcreate')
tset.start()
tprint.start()
tcreate.start()

全文完


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 青青青国产在线视频 | 亚洲在线高清 | 污夜影院| 高清视频一区二区 | 免费h片在线观看 | 亚洲国产模特在线播放 | 中文字幕日韩在线观看 | 亚洲日产综合欧美一区二区 | 日本中文字幕在线视频 | 午夜三级做爰视频在线看 | 日韩伦理一区二区三区 | 欧美午夜性春 | 日韩免费一级片 | 性欧美高清久久久久久久 | 视频一区久久 | 成人a免费视频播放 | 日本黄色小视频在线观看 | 毛片黄片视频 | 看黄a大片 免费 | 毛片黄片视频 | 亚洲视频一区在线播放 | 乱人伦精品一区二区 | 九九九精品视频免费 | 在线91精品亚洲网站精品成人 | 老子影院午夜伦不卡不四虎卡 | 久久精品阿娇 | 免费观看日本特色做爰视频在线 | 日本特黄a级高清免费大片18 | 九九久久国产 | 九九99线视频在线观看 | 亚洲美女在线视频 | 久久福利资源网站免费看 | 日本高清h色视频在线观看 日本高清不卡二区 | 国产欧美日韩精品一区二 | 免费区欧美一级毛片精品 | 中国女人69xxxxx| 免费不卡毛片 | 亚洲国产综合视频 | 午夜黄色影院 | 日韩中文精品亚洲第三区 | a免费在线观看视频 |