python線程開發

  線程(有時被稱爲輕量級進程)跟進程有些相似,不同的是所有的線程運行在同一個進程中,共享相同的運行環境。它們可以想像成是在主進程或“主線程”中並行運行的“迷你進程”。線程有開始、順序執行和結束三部分,它有一個自己的指令指針,記錄自己運行到什麼地方。線程的運行可能被搶佔(中斷)或暫時的被掛起(也叫睡眠)讓其它的線程運行,這叫做讓步。一個進程中的各個線程之間共享同一片數據空間,所以線程之間可以比進程之間更方便地共享數據以及相互通訊。線程一般都是併發執行的,正是由於這種並行和數據共享的機制使得多個任務的合作變爲可能。實際上,在單CPU的系統中,真正的併發是不可能的,每個線程會被安排成每次只運行一小會,然後就把CPU讓出來,讓其它的線程去運行。在進程的整個運行過程中,每個線程都只做自己的事,在需要的時候跟其它的線程共享運行的結果。多個線程共同訪問同一片數據不是完全沒有危險的,由於數據訪問的順序不一樣,有可能導致數據結果的不一致的問題,這叫做競態條件。而大多數線程庫都帶有一系列的同步原語,來控制線程的執行和數據的訪問。

    Python全局鎖 

    Python代碼的執行由Python虛擬機(也叫解釋器主循環)來控制。Python在設計之初就考慮到要在主循環中,同時只有一個線程在執行。雖然 Python 解釋器中可以“運行”多個線程,但在任意時刻只有一個線程在解釋器中運行。   

     對Python虛擬機的訪問由全局解釋器鎖(GIL)來控制,正是這個鎖能保證同一時刻只有一個線程在運行。在多線程環境中,Python 虛擬機按以下方式執行:a、設置 GIL;b、切換到一個線程去運行;c、運行指定數量的字節碼指令或者線程主動讓出控制(可以調用 time.sleep(0));d、把線程設置爲睡眠狀態;e、解鎖 GIL;d、再次重複以上所有步驟。

    任何Python線程執行前,必須先獲得GIL鎖,然後,每執行100條字節碼,解釋器就自動釋放GIL鎖,讓別的線程有機會執行。這個GIL全局鎖實際上把所有線程的執行代碼都給上了鎖,所以,多線程在Python中只能交替執行,即使100個線程跑在100核CPU上,也只能用到1個核。


例子:

1、


import threading

import time

def haha(max_num):

    for i in range(max_num):

        time.sleep(1)

        print i

for x in range(3):

    """

    這裏的rang(3)是要依次啓動三個線程,每個線程都調用函數haha()

    第一個線程啓動執行之後,馬上啓動第二個線程再次執行。最後也相當於

    函數執行了3次

    """

    #通過threading.Thread方法實例化多線程類

    #target後面跟的是函數的名稱但是不要帶括號也不填寫參數

    #args後面的內容纔是要傳遞給函數haha()的參數。切記參數一定要以元組的形式填寫不然會報錯。

    t=threading.Thread(target=haha,args=(10,))

    #將線程設置爲守護線程

    t.setDaemon(True)

    #線程準備就緒,隨時等候cpu調度

    t.start()

 

 setDaemon()

   如果是後臺線程,主線程執行過程中,後臺線程也在進行,主線程執行完畢後,後臺線程不論成功與否,均停止

     如果是前臺線程,主線程執行過程中,前臺線程也在進行,主線程執行完畢後,等待前臺線程也執行完成後,程序停止


 2、.join()的用法


 import threading

import time

def haha(max_num):

    for i in range(max_num):

        time.sleep(1)

        print i

"""

創建一個列表,用於存儲要啓動多線程的實例

"""

threads=[]

for x in range(3):

    t=threading.Thread(target=haha,args=(5,))

    #把多線程的實例追加入列表,要啓動幾個線程就追加幾個實例

    threads.append(t)

for thr in threads:

    #把列表中的實例遍歷出來後,調用start()方法以線程啓動運行

    thr.start()

for thr in threads:

    """

    isAlive()方法可以返回True或False,用來判斷是否還有沒有運行結束

    的線程。如果有的話就讓主線程等待線程結束之後最後再結束。

    """

    if thr.isAlive():

        thr.join()

     

     程序運行的時候最先啓動的一定就是主線程,主線程負責拉起子線程用於幹活。我們的例子中運行函數haha()線程其實都是子線程。因此可以說多線程其實就是多個子線程。那麼程序運行完最後一個退出的也肯定就是主線程。因此上例中最後再遍歷一個遍threads列表的目的就是查看還是否有沒有退出的子線程,只要還有子線程是活的,沒有退出。就通過join()方法強制程序流程不可以走到主線程退出的那個步驟。只有等子線程都退出之後,才能根據join()方法的規則順序執行到主線程退出的步驟。

     

3、通過類來實現線程


import threading

import time

class haha(threading.Thread):

    """

    自定義一個類haha,必須要繼承threading.Thread,下面必須要重寫一個run()方法。

    把要執行的函數寫到run()方法裏。如果沒有run()方法就會報錯。其實這個類的作用就是

    通過haha類裏面的run()方法來定義每個啓動的子線程要執行的函數內容。

    """

    

    def __init__(self,max_num):

    

        threading.Thread.__init__(self)

    

        self.max_num=max_num

    

    def run(self):

    

        for i in range(self.max_num):

    

            time.sleep(1)

    

            print i


if __name__==‘__main__‘:


        threads=[]


        for x in range(3):

        """

        只是這裏和函數方式有點區別,因爲haha類繼承了threading.Thread,所以通過haha類的實例化

        就相當於調用了多線程的實例化。剩下的操作就和函數方式一個樣子了。

        """

        t=haha(5)

       

        threads.append(t)

    

        for thr in threads:

            

             thr.start()

        

        for thr in threads:

        

        if thr.isAlive():

        

            thr.join()




4、線程鎖

import threading


gnum=0


lock=threading.RLock()


def work(max_number):

  

    for i in range(max_number):

      

        print i


def mylock():

    

    work(10)

   

    #在操作gnum之前先上鎖

    

    #acquire()的括號裏可以定義鎖定的timeout時間,超過這個時間就自動打開鎖

   

    lock.acquire()

   

    global gnum

    

    gnum=gnum+1


    #操作結束之後再打開鎖

    lock.release()

  

    print ‘gnum is ‘,gnum


for x in range(5):

   

    t=threading.Thread(target=mylock)

    

    t.start()

    

     什麼是線程鎖呢?就是在多個線程同時操作一個資源的時候,哪個線程先操作。哪個線程就先鎖定這個資源。直到這個線程操作結束打開鎖之後,其他的線程才能再操作。這就叫做線程安全,也就是線程鎖,上例子如果不加線程鎖,得到的gnum的結果會得不到預期,線程操作是同時的,多個線程操作同一個數據。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章