Python多線程之線程鎖(Lock)和遞歸鎖(RLock)實例

  • 2020 年 2 月 10 日
  • 筆記

一、線程鎖

Threading模塊為我們提供了一個類,Threading.Lock鎖。我們創建一個該類對象,在線程函數執行前,「搶佔」該鎖,執行完成後,「釋放」該鎖,則我們確保了每次只有一個線程佔有該鎖。這時候對一個公共的對象進行操作,則不會發生線程不安全的現象了。

1、我們先建立了一個threading.Lock類對象lock,在run方法里,我們使用lock.acquire()獲得了這個鎖。此時,其他的線程就無法再獲得該鎖了,他們就會阻塞在「if lock.acquire()」這裡,直到鎖被另一個線程釋放:lock.release()。

2、如果多個線程要調用多個現象,而A線程調用A鎖佔用了A對象,B線程調用了B鎖佔用了B對象,A線程不能調用B對象,B線程不能調用A對象,於是一直等待。這就造成了線程「死鎖」。

Threading模塊中,也有一個類,RLock,稱之為可重入鎖。該鎖對象內部維護着一個Lock和一個counter對象。counter對象記錄了acquire的次數,使得資源可以被多次require。最後,當所有RLock被release後,其他線程才能獲取資源。在同一個線程中,RLock.acquire可以被多次調用,利用該特性,可以解決部分死鎖問題

3、當多個線程同時訪問一個數據時,需加鎖,排隊變成單線程一個一個執行

4、加鎖避免並發導致邏輯出錯

5、每當一個線程a要訪問共享數據時,必須先獲得鎖定;如果已經有別的線程b獲得鎖定了,那麼就讓線程a暫停,也就是同步阻塞;等到線程b訪問完畢,釋放鎖以後,再讓線程a繼續

6、語法

lock=threading.Lock() #創建線程鎖

lock = threading.RLock()#創建遞歸鎖(多個鎖時用這個)

lock.acquire() #鎖住

lock.release() 釋放鎖

二、線程鎖實例

#未加鎖  如果多個線程同時操作某個數據,會出現不可預料的結果。比如以下場景:當小夥伴a在往火鍋裏面添加魚丸的時候,小夥伴b在同時吃掉魚丸,這很有可能導致剛下鍋的魚丸被夾出來了(沒有熟),或者還沒下鍋,就去夾魚丸(夾不到)      # coding=utf-8  import threading  import time  def chiHuoGuo(people, do):      print("%s 吃火鍋的小夥伴:%s" % (time.ctime(),people))      time.sleep(1)      for i in range(3):          time.sleep(1)          print("%s %s正在 %s 魚丸"% (time.ctime(), people, do))      print("%s 吃火鍋的小夥伴:%s" % (time.ctime(),people))  class myThread (threading.Thread):   # 繼承父類threading.Thread      def __init__(self, people, name, do):          '''重寫threading.Thread初始化內容'''          threading.Thread.__init__(self)          self.threadName = name          self.people = people          self.do = do      def run(self):   # 把要執行的代碼寫到run函數裏面 線程在創建後會直接運行run函數          '''重寫run方法'''          print("開始線程: " + self.threadName)          chiHuoGuo(self.people, self.do)     # 執行任務          print("結束線程: " + self.name)  print("yoyo請小夥伴開始吃火鍋:!!!")  # 設置線程組  threads = []  # 創建新線程  thread1 = myThread("xiaoming", "Thread-1", "添加")  thread2 = myThread("xiaowang", "Thread-2", "吃掉")  # 添加到線程組  threads.append(thread1)  threads.append(thread2)  # 開啟線程  for thread in threads:      thread.start()  # 阻塞主線程,等子線程結束  for thread in threads:      thread.join()  time.sleep(0.1)  print("退出主線程:吃火鍋結束,結賬走人")      C:UserswangliPycharmProjectsAutoMationvenvScriptspython.exe C:/Users/wangli/PycharmProjects/AutoMation/case/test.py  yoyo請小夥伴開始吃火鍋:!!!  開始線程: Thread-1  Fri Mar 15 08:33:19 2019 吃火鍋的小夥伴:xiaoming  開始線程: Thread-2  Fri Mar 15 08:33:19 2019 吃火鍋的小夥伴:xiaowang  Fri Mar 15 08:33:21 2019 xiaowang正在 吃掉 魚丸  Fri Mar 15 08:33:21 2019 xiaoming正在 添加 魚丸  Fri Mar 15 08:33:22 2019 xiaowang正在 吃掉 魚丸  Fri Mar 15 08:33:22 2019 xiaoming正在 添加 魚丸  Fri Mar 15 08:33:23 2019 xiaowang正在 吃掉 魚丸  Fri Mar 15 08:33:23 2019 吃火鍋的小夥伴:xiaowang  結束線程: Thread-2  Fri Mar 15 08:33:23 2019 xiaoming正在 添加 魚丸  Fri Mar 15 08:33:23 2019 吃火鍋的小夥伴:xiaoming  結束線程: Thread-1  退出主線程:吃火鍋結束,結賬走人    Process finished with exit code 0
#線程鎖,單鎖實例  import time,threading  def run(n):      lock.acquire() #加鎖      global num      num+=1      lock.release() #釋放鎖  lock=threading.Lock()#獲得線程鎖  num=0  threads=[]  for i in range(50):      thread=threading.Thread(target=run,args=("t-%s"%i,))      thread.start()      threads.append(thread)  for i in threads:      i.join()  print("num:",num)      C:UserswangliPycharmProjectsAutoMationvenvScriptspython.exe C:/Users/wangli/PycharmProjects/AutoMation/case/test.py  num: 50    Process finished with exit code 0
#線程鎖,加鎖實例    # coding=utf-8  import threading  import time  def chiHuoGuo(people, do):      print("%s 吃火鍋的小夥伴:%s" % (time.ctime(),people))      time.sleep(1)      for i in range(3):          time.sleep(1)          print("%s %s正在 %s 魚丸"% (time.ctime(), people, do))      print("%s 吃火鍋的小夥伴:%s" % (time.ctime(),people))  class myThread (threading.Thread):   # 繼承父類threading.Thread      lock = threading.Lock()  # 線程鎖      def __init__(self, people, name, do):          '''重寫threading.Thread初始化內容'''          threading.Thread.__init__(self)          self.threadName = name          self.people = people          self.do = do      def run(self):   # 把要執行的代碼寫到run函數裏面 線程在創建後會直接運行run函數          '''重寫run方法'''          print("開始線程: " + self.threadName)          # 執行任務之前鎖定線程          self.lock.acquire()          chiHuoGuo(self.people, self.do)     # 執行任務          # 執行完之後,釋放鎖          self.lock.release()          print("結束線程: " + self.name)  print("yoyo請小夥伴開始吃火鍋:!!!")  # 設置線程組  threads = []  # 創建新線程  thread1 = myThread("xiaoming", "Thread-1", "添加")  thread2 = myThread("xiaowang", "Thread-2", "吃掉")  # 添加到線程組  threads.append(thread1)  threads.append(thread2)  # 開啟線程  for thread in threads:      thread.start()  # 阻塞主線程,等子線程結束  for thread in threads:      thread.join()  time.sleep(0.1)  print("退出主線程:吃火鍋結束,結賬走人")      C:UserswangliPycharmProjectsAutoMationvenvScriptspython.exe C:/Users/wangli/PycharmProjects/AutoMation/case/test.py  yoyo請小夥伴開始吃火鍋:!!!  開始線程: Thread-1  Fri Mar 15 08:36:39 2019 吃火鍋的小夥伴:xiaoming  開始線程: Thread-2  Fri Mar 15 08:36:41 2019 xiaoming正在 添加 魚丸  Fri Mar 15 08:36:42 2019 xiaoming正在 添加 魚丸  Fri Mar 15 08:36:43 2019 xiaoming正在 添加 魚丸  Fri Mar 15 08:36:43 2019 吃火鍋的小夥伴:xiaoming  結束線程: Thread-1  Fri Mar 15 08:36:43 2019 吃火鍋的小夥伴:xiaowang  Fri Mar 15 08:36:45 2019 xiaowang正在 吃掉 魚丸  Fri Mar 15 08:36:47 2019 xiaowang正在 吃掉 魚丸  Fri Mar 15 08:36:48 2019 xiaowang正在 吃掉 魚丸  Fri Mar 15 08:36:48 2019 吃火鍋的小夥伴:xiaowang  結束線程: Thread-2  退出主線程:吃火鍋結束,結賬走人    Process finished with exit code 0
#線程鎖,多個鎖時,需加遞歸鎖    import threading, time  def run1():      print("grab the first part data")      lock.acquire()      global num      num += 1      lock.release()      return num  def run2():      print("grab the second part data")      lock.acquire()      global num2      num2 += 1      lock.release()      return num2  def run3():      lock.acquire()      res = run1()      print('--------between run1 and run2-----')      res2 = run2()      lock.release()      print(res, res2)  num, num2 = 0, 0  lock = threading.RLock() #遞歸鎖  for i in range(3):      t = threading.Thread(target=run3)      t.start()  while threading.active_count() != 1:      print(threading.active_count())  else:      print('----all threads done---')      print(num, num2)