上(shang)一篇博客介(jie)(jie)紹(shao)了Python并發編(bian)程的(de)(de)基本知(zhi)識,感興(xing)趣的(de)(de)可以(yi)(yi)到專利找(zhao)到上(shang)一篇文章,本文主(zhu)要介(jie)(jie)紹(shao)如何(he)開啟多線(xian)程,以(yi)(yi)及多線(xian)程在(zai)提升效率的(de)(de)同時會存在(zai)哪些問題
Python通過標準庫的 threading 模(mo)塊來管理線程。這個模(mo)塊提供(gong)了很多不(bu)錯的特(te)性,讓(rang)線程變得無比簡(jian)單,打開(kai)threading.py文件,可(ke)以(yi)到(dao)如下(xia)類或者方(fang)法

線(xian)程(cheng)(cheng)模塊常用(yong)的(de)主要組件有,Thread、Event、Lock、RLock、Semaphore、Timer等等,他們可以用(yong)來(lai)開啟線(xian)程(cheng)(cheng)、保證線(xian)程(cheng)(cheng)間的(de)安全、線(xian)程(cheng)(cheng)間的(de)通信等等
使用線(xian)程最簡單的一個(ge)(ge)方法是,用一個(ge)(ge)目標函數實例化(hua)一個(ge)(ge)Thread然后調用 start() 方法啟動它。示例如下(xia)
import threading
def function(i):
    print ("function called by thread %i\n" % i)
    return
threads = []
for i in range(5):
    t = threading.Thread(target=function , args=(i, ))
    threads.append(t)
    t.start()
    t.join()上述代碼就可以開啟(qi)5個(ge)線程執行(xing)function函數
Thread()參數解釋如(ru)下
- 
group: 一般(ban)設置為None,這(zhe)是(shi)為以(yi)后的一(yi)些特性預留的
- 
target: 當線程啟動的(de)(de)時候要執行的(de)(de)函數(shu)
- 
name: 線程(cheng)的名字,默認(ren)會分配(pei)一個唯一名字Thread-N
- 
args: 傳遞給target的參數,要使用tuple類型
- 
kwargs: 同上,使用字(zi)典類(lei)型(xing)dict
Thread類(lei)定義(yi)的常(chang)用方法(fa)如下(xia)
- 
start(): 啟動線(xian)程,并(bing)執行run()方法。
- 
run(): 線程啟動后執行的方法,可以在(zai)子類中(zhong)重寫(xie)。
- 
join([timeout]): 等待線程結束,可選參數timeout指定最長等待時間(jian)。
- 
is_alive(): 判斷線程(cheng)是(shi)否仍然存(cun)活。
- 
name: 線(xian)程名(ming)稱的(de)屬性,可以在實例化時指定或修改。
- 
setDaemon(daemonic): 設置線程是否為守(shou)護線程,默認為 False。守(shou)護線程會在(zai)主線程結束(shu)時自動退出。
- 
getName(): 獲(huo)取線程(cheng)名稱。
- 
setName(name): 設置線程名稱(cheng)。
多個(ge)(ge)線程操作同一個(ge)(ge)資源,并且至少有(you)一個(ge)(ge)可(ke)以改變數據,又沒有(you)同步機(ji)制的條件(jian)下,就會產生競(jing)爭(zheng)條件(jian),可(ke)能(neng)會導致執(zhi)行(xing)無效代碼、bug、或異常行(xing)為。
這時候可(ke)以用Lock來(lai)保(bao)持線程(cheng)同步,例(li)如下(xia)列(lie)代碼,在下(xia)面(mian)的代碼中,我們(men)有(you)兩(liang)個(ge)函數(shu): increment() 和 decrement() 。第一(yi)個(ge)(ge)(ge)函(han)數對共(gong)享資(zi)源執行加(jia)1的操作,另一(yi)個(ge)(ge)(ge)函(han)數執行減1.兩(liang)個(ge)(ge)(ge)函(han)數分別使用線程(cheng)封裝。除此之外,每一(yi)個(ge)(ge)(ge)函(han)數都有一(yi)個(ge)(ge)(ge)循環重復執行操作。我們想要保證,通過對共(gong)享資(zi)源的管理,執行結(jie)果是共(gong)享資(zi)源最后等(deng)于初始值0.
# -*- coding: utf-8 -*-
import threading
shared_resource_with_lock = 0
shared_resource_with_no_lock = 0
COUNT = 10000000
shared_resource_lock = threading.Lock()
# 有鎖的情況
def increment_with_lock():
    global shared_resource_with_lock
    for i in range(COUNT):
        shared_resource_lock.acquire()
        shared_resource_with_lock += 1
        shared_resource_lock.release()
def decrement_with_lock():
    global shared_resource_with_lock
    for i in range(COUNT):
        shared_resource_lock.acquire()
        shared_resource_with_lock -= 1
        shared_resource_lock.release()
# 沒有鎖的情況
def increment_without_lock():
    global shared_resource_with_no_lock
    for i in range(COUNT):
        shared_resource_with_no_lock += 1
def decrement_without_lock():
    global shared_resource_with_no_lock
    for i in range(COUNT):
        shared_resource_with_no_lock -= 1
if __name__ == "__main__":
    t1 = threading.Thread(target=increment_with_lock)
    t2 = threading.Thread(target=decrement_with_lock)
    t3 = threading.Thread(target=increment_without_lock)
    t4 = threading.Thread(target=decrement_without_lock)
    t1.start()
    t2.start()
    t3.start()
    t4.start()
    t1.join()
    t2.join()
    t3.join()
    t4.join()
    print ("the value of shared variable with lock management is %s" % shared_resource_with_lock)
    print ("the value of shared variable with race condition is %s" % shared_resource_with_no_lock)輸出
the value of shared variable with lock management is 0
the value of shared variable with race condition is -242686可(ke)以看到(dao)未加鎖的程序(xu)出(chu)(chu)現了錯誤(wu),如(ru)果你運行了示例代碼沒有出(chu)(chu)錯,可(ke)以把COUNT值調(diao)大試試
但加鎖有什么缺點(dian)呢,第(di)一會(hui)消耗資(zi)源,第(di)二如(ru)果程序中(zhong)有多(duo)個(ge)鎖存在,可能會(hui)造成死鎖,導致程序一直卡住,我們設有兩個(ge)并發的線程( 線程A 和 線程B ),需要 資源1 和 資(zi)源2 .假(jia)設 線(xian)程A 需要 資源1 , 線程B 需要 資源2 .在(zai)這種(zhong)情況下,兩(liang)個線程都使用各(ge)自的(de)鎖,目前(qian)為止沒有沖突。現(xian)在(zai)假(jia)設,在(zai)雙方釋放鎖之前(qian), 線程A 需(xu)要 資源2 的(de)鎖, 線程B 需(xu)要 資源1 的鎖,沒有資源線程不會繼續執行(xing)。這就出現了死鎖問題。
下篇文章介紹如何解決(jue)多線程的死鎖問題(ti)