多任務-多線程

注意:
因爲python存在全局解釋器鎖(GIL)所以純Cpython不存在真正的多任務。想要實現多任務可以使用c模塊實現真正的多任務。(筆記 多任務-* 不考慮GIL的存在,認爲python存在多任務)。

每個進程默認開啓一條主線程可以自己開闢多條子線程
使用 threading 可是創建線程 threading 是對較底層模塊thread的進一步封裝

簡單多線程

  1. def 創建一個函數用於開闢線程,開闢的線程執行此函數
  2. 使用threading.Thread() 創建線程
  3. 使用start()啓動該線程
  4. 基本代碼實現
    import threading
    import time
    
    def saySorry():
        print("親愛的,我錯了,我能吃飯了嗎?")
        time.sleep(1)
    
    if __name__ == "__main__":
        for i in range(5):
            # target=saySorry使用函數的引用,不加()加上括號表示直接執行
            t = threading.Thread(target=saySorry)
            t.start() #啓動線程,即讓線程開始執行 `
  5. 注意點:
    1. 當調用start()時,纔會真正的創建線程,並且開始執行
    2. 主線程是默認等待子線程執行結束主線程才結束執行的
  6. 對線程的封裝
    1. 爲了讓每個線程封裝的更完美使用threading是可以創建類繼承自threading.Thread然後重寫run方法即可
    2. 代碼實現
      import threading
      import time
      
      class MyThread(threading.Thread):
          def run(self):
              for i in range(3):
                  time.sleep(1)
                  msg = "I'm "+self.name+' @ '+str(i) #name屬性中保存的是當前線程的名字
                  print(msg)
      
      
      if __name__ == '__main__':
          t = MyThread()
          t.start()
    3. python的threading.Thread類有一個run方法,用於定義線程的功能函數,可以在自己的線程類中覆蓋該方法。而創建自己的線程實例後,通過Thread類的start方法,可以啓動該線程,交給python虛擬機進行調度,當該線程獲得執行的機會時,就會調用run方法執行線程。
  7. 線程的調度是無序的,當線程中有耗時操作類似(sleep)時會阻塞線程選擇器會自動選擇一個就緒線程進行執行,進而執行別的線程當sleep結束後會進入就緒狀態等待被調用。
  8. 小結
    1. 每個線程默認有一個名字,但是python會自動爲線程指定一個名字。
    2. 當線程的run()方法結束時該線程完成。
    3. 無法控制線程調度程序,但可以通過別的方式來影響線程調度的方式。

互斥鎖

  1. 引入:同一進程中線共享全局變量,這樣方便多線程之間共享數據。所以存在某一變量同時被多個線程調用,此時就會發生資源競爭問題導致變量數據的錯亂。例如兩個進程同時對一個變量進行累計 “1”操作是就會發生數據錯亂,當每個進程循環多次此操作是效果明顯。於是引入互斥鎖概念
  2. 互斥鎖狀態分爲:鎖定/非鎖定
  3. 當一個進程調用某一變量時先將其鎖定,此時其他進程不能調用。當且僅當該進程釋放資源是其他進程才能調該資源。互斥鎖通過保證每次只有一個進程對資源的操作,從而保證多線程情況下數據的正確性
  4. 使用互斥鎖是要避免死鎖的存在(進程A需要調用變量a、b,進程B也需要調用變量a,b。當A先鎖定a事等待b的釋放,此時B鎖定b等待a的釋放此時會發生彼此等待會發生死鎖現象。A、B都等待對方釋放資源)
  5. 使用互斥鎖:
    1. 使用threading模塊中的Lock類實例化鎖對象
    2. 使用acquire()加鎖
    3. 執行操作
    4. 使用release()釋放
    5. 注意:如果這個鎖之前是沒有上鎖的,那麼acquire不會堵塞。如果在調用acquire對這個鎖上鎖之前 它已經被 其他線程上了鎖,那麼此時acquire會堵塞,直到這個鎖被解鎖爲止
  6. 互斥鎖鎖的是誰:個人認爲是對變量進行加鎖,當對線程加鎖時鎖定加鎖處到釋放鎖處用到的全局變量。其他線程無權調用只有當釋放鎖是被加鎖全局變量才能被其他線程調用(僅代表個人理解
  7. 互斥鎖的代碼實現
    import threading
    import time
    
    
    g_num = 0
    
    def test1(num):
        global g_num
        for i in range(num):
            mutex.acquire()  # 上鎖
            g_num += 1
            mutex.release()  # 解鎖
    
        print("---test1---g_num=%d"%g_num)
    
    def test2(num):
        global g_num
        for i in range(num):
            mutex.acquire()  # 上鎖
            g_num += 1
            mutex.release()  # 解鎖
    
        print("---test2---g_num=%d"%g_num)
    
    # 創建一個互斥鎖
    # 默認是未上鎖的狀態
    mutex = threading.Lock()
    
    # 創建2個線程,讓他們各自對g_num加1000000次
    p1 = threading.Thread(target=test1, args=(1000000,))
    p1.start()
    
    p2 = threading.Thread(target=test2, args=(1000000,))
    p2.start()
    
    # 等待計算完成
    while len(threading.enumerate()) != 1:
        time.sleep(1)
    
    print("2個線程對同一個全局變量操作之後的最終結果是:%s" % g_num)

 

錯誤之處歡迎指出

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