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)