麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 學(xué)院 > 開發(fā)設(shè)計 > 正文

Python:線程指南

2019-11-14 17:41:12
字體:
供稿:網(wǎng)友

1. 線程基礎(chǔ)

1.1. 線程狀態(tài)

線程有5種狀態(tài),狀態(tài)轉(zhuǎn)換的過程如下圖所示:

thread_stat_simple

1.2. 線程同步(鎖)

多線程的優(yōu)勢在于可以同時運行多個任務(wù)(至少感覺起來是這樣)。但是當(dāng)線程需要共享數(shù)據(jù)時,可能存在數(shù)據(jù)不同步的問題。考慮這樣一種情況:一個列表里所有元素都是0,線程"set"從后向前把所有元素改成1,而線程"

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

thread_lock

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

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

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

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

thread_condition_wait  

thread_condition_notify

1.4. 線程運行和阻塞的狀態(tài)轉(zhuǎn)換

最后看看線程運行和阻塞狀態(tài)的轉(zhuǎn)換。

thread_stat

阻塞有三種情況: 
同步阻塞是指處于競爭鎖定的狀態(tài),線程請求鎖定時將進入這個狀態(tài),一旦成功獲得鎖定又恢復(fù)到運行狀態(tài); 
等待阻塞是指等待其他線程通知的狀態(tài),線程獲得條件鎖定后,調(diào)用“等待”將進入這個狀態(tài),一旦其他線程發(fā)出通知,線程將進入同步阻塞狀態(tài),再次競爭條件鎖定; 
而其他阻塞是指調(diào)用time.sleep()、anotherthread.join()或等待IO時的阻塞,這個狀態(tài)下線程不會釋放已獲得的鎖定。

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

2. thread

Python通過兩個標(biāo)準(zhǔn)庫thread和threading提供對線程的支持。thread提供了低級別的、原始的線程以及一個簡單的鎖。

# encoding: UTF-8import threadimport time # 一個用于在線程中執(zhí)行的函數(shù)def func():    for i in range(5):        print 'func'        time.sleep(1)        # 結(jié)束當(dāng)前線程    # 這個方法與thread.exit_thread()等價    thread.exit() # 當(dāng)func返回時,線程同樣會結(jié)束        # 啟動一個線程,線程立即開始運行# 這個方法與thread.start_new_thread()等價# 第一個參數(shù)是方法,第二個參數(shù)是方法的參數(shù)thread.start_new(func, ()) # 方法沒有參數(shù)時需要傳入空tuple # 創(chuàng)建一個鎖(LockType,不能直接實例化)# 這個方法與thread.allocate_lock()等價lock = thread.allocate() # 判斷鎖是鎖定狀態(tài)還是釋放狀態(tài)print lock.locked() # 鎖通常用于控制對共享資源的訪問count =# 獲得鎖,成功獲得鎖定后返回True# 可選的timeout參數(shù)不填時將一直阻塞直到獲得鎖定# 否則超時后將返回Falseif lock.acquire():    count += 1        # 釋放鎖    lock.release() # thread模塊提供的線程都將在主線程結(jié)束后同時結(jié)束time.sleep(6)

thread 模塊提供的其他方法: 

thread.interrupt_main(): 在其他線程中終止主線程。 
thread.get_ident(): 獲得一個代表當(dāng)前線程的魔法數(shù)字,常用于從一個字典中獲得線程相關(guān)的數(shù)據(jù)。這個數(shù)字本身沒有任何含義,并且當(dāng)線程結(jié)束后會被新線程復(fù)用。

thread還提供了一個ThreadLocal類用于管理線程相關(guān)的數(shù)據(jù),名為 thread._local,threading中引用了這個類。

由于thread提供的線程功能不多,無法在主線程結(jié)束后繼續(xù)運行,不提供條件變量等等原因,一般不使用thread模塊,這里就不多介紹了。

3. threading

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

threading 模塊提供的常用方法: 
threading.currentThread(): 返回當(dāng)前的線程變量。 
threading.enumerate(): 返回一個包含正在運行的線程的list。正在運行指線程啟動后、結(jié)束前,不包括啟動前和終止后的線程。 
threading.activeCount(): 返回正在運行的線程數(shù)量,與len(threading.enumerate())有相同的結(jié)果。

threading模塊提供的類:  
Thread, Lock, Rlock, Condition, [Bounded]Semaphore, Event, Timer, local.

3.1. Thread

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

# encoding: UTF-8import threading # 方法1:將要執(zhí)行的方法作為參數(shù)傳給Thread的構(gòu)造方法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()

構(gòu)造方法: 

Thread(group=None, target=None, name=None, args=(), kwargs={}) 
group: 線程組,目前還沒有實現(xiàn),庫引用中提示必須是None; 
target: 要執(zhí)行的方法; 
name: 線程名; 
args/kwargs: 要傳入方法的參數(shù)。

實例方法: 
isAlive(): 返回線程是否在運行。正在運行指啟動后、終止前。 
get/setName(name): 獲取/設(shè)置線程名。 
is/setDaemon(bool): 獲取/設(shè)置是否守護線程。初始值從創(chuàng)建該線程的線程繼承。當(dāng)沒有非守護線程仍在運行時,程序?qū)⒔K止。 
start(): 啟動線程。 
join([timeout]): 阻塞當(dāng)前上下文環(huán)境的線程,直到調(diào)用此方法的線程終止或到達指定的timeout(可選參數(shù))。

一個使用join()的例子:

# encoding: UTF-8import threadingimport time def context(tJoin):    print 'in threadContext.'    tJoin.start()        # 將阻塞tContext直到threadJoin終止。    tJoin.join()        # tJoin終止后繼續(xù)執(zhí)行。    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()

運行結(jié)果:

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

3.2. Lock

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

可以認(rèn)為Lock有一個鎖定池,當(dāng)線程請求鎖定時,將線程至于池中,直到獲得鎖定后出池。池中的線程處于狀態(tài)圖中的同步阻塞狀態(tài)。

構(gòu)造方法: 
Lock()

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

# encoding: UTF-8import threadingimport time data = 0lock = threading.Lock() def func():    global data    print '%s acquire lock...' % threading.currentThread().getName()        # 調(diào)用acquire([timeout])時,線程將一直阻塞,    # 直到獲得鎖定或者直到timeout秒后(timeout參數(shù)可選)。    # 返回是否獲得鎖。    if lock.acquire():        print '%s get the lock.' % threading.currentThread().getName()        data += 1        time.sleep(2)        print '%s release lock...' % threading.currentThread().getName()                # 調(diào)用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使用了“擁有的線程”和“遞歸等級”的概念,處于鎖定狀態(tài)時,RLock被某個線程擁有。擁有RLock的線程可以再次調(diào)用acquire(),釋放鎖時需要調(diào)用release()相同次數(shù)。

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

構(gòu)造方法: 
RLock()

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

# encoding: UTF-8import threadingimport 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(條件變量)通常與一個鎖關(guān)聯(lián)。需要在多個Contidion中共享一個鎖時,可以傳遞一個Lock/RLock實例給構(gòu)造方法,否則它將自己生成一個RLock實例。

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

構(gòu)造方法: 
Condition([lock/rlock])

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

例子是很常見的生產(chǎn)者/消費者模式:

# encoding: UTF-8import threadingimport time # 商品product = None# 條件變量con = threading.Condition() # 生產(chǎn)者方法def produce():    global product        if con.acquire():        while True:            if product is None:                print 'produce...'                product = 'anything'                                # 通知消費者,商品已經(jīng)生產(chǎn)                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                                # 通知生產(chǎn)者,商品已經(jīng)沒了                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(信號量)是計算機科學(xué)史上最古老的同步指令之一。Semaphore管理一個內(nèi)置的計數(shù)器,每當(dāng)調(diào)用acquire()時-1,調(diào)用release() 時+1。計數(shù)器不能小于0;當(dāng)計數(shù)器為0時,acquire()將阻塞線程至同步鎖定狀態(tài),直到其他線程調(diào)用release()。

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

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

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

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

# encoding: UTF-8import threadingimport time # 計數(shù)器初值為2semaphore = threading.Semaphore(2def func():        # 請求Semaphore,成功后計數(shù)器-1;計數(shù)器為0時阻塞    print '%s acquire semaphore...' % threading.currentThread().getName()    if semaphore.acquire():                print '%s get semaphore' % threading.currentThread().getName()        time.sleep(4)                # 釋放Semaphore,計數(shù)器+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的主線程也可以調(diào)用release# 若使用BoundedSemaphore,t4釋放semaphore時將拋出異常print 'MainThread release semaphore without acquire'semaphore.release()

3.6. Event

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

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

構(gòu)造方法: 
Event()

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

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

3.7. Timer

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

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

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

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

3.8. local

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

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

# encoding: UTF-8import 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就可以應(yīng)對絕大多數(shù)需要使用線程的場合,某些情況下local也是非常有用的東西。本文的最后使用這幾個類展示線程基礎(chǔ)中提到的場景:

# encoding: UTF-8import threading alist = Nonecondition = 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()

原文地址:http://www.companysz.com/huxi/archive/2010/06/26/1765808.html


上一篇:Python基礎(chǔ)

下一篇:python屬性查找

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 在线观看视频毛片 | 久在线观看福利视频69 | 精品一区二区久久久 | 欧美日在线观看 | 亚洲精品xxx | 91久久国产露脸精品国产护士 | 性欧美极品xxxx欧美一区二区 | 久久国产精品一区 | 亚洲一区二区免费视频 | 蝌蚪久久窝 | 羞羞漫画无遮挡观看 | 毛片一级免费看 | 一级免费特黄视频 | 亚洲精品欧美二区三区中文字幕 | 在线看毛片的网站 | 久久久免费 | 在线成人精品视频 | 蜜桃成品人免费视频 | 欧美人成在线 | 视频一区二区在线观看 | 一区二区三区视频在线观看 | 午夜精品福利在线观看 | 久久久久久久久久久久网站 | 国产一级aa大片毛片 | 一夜新娘第三季免费观看 | 一级啪啪片 | 久久久久久久免费视频 | 精品一区二区三区免费毛片爱 | 日韩在线毛片 | 欧美视频在线一区二区三区 | 羞羞网站在线观看入口免费 | 欧美性生交大片 | 激情小说激情图片激情电影 | 97精品视频在线观看 | 国产亚洲精品久久久久久久久 | 视屏一区 | 在线高清中文字幕 | 国产在线一区二区三区 | 成人情欲视频在线看免费 | wwwxxx视频 | 精品三区视频 |