threading模块中的Lock、RLock以及Condition

一、互斥锁

  • 简介

    • 互斥锁为资源引入一个状态:锁定/非锁定。
    • 某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。
  • 上锁解锁过程

    • 当一个线程调用锁的acquire()方法获得锁时,锁就进入锁定状态
    • 如果此时另一个线程试图通过acquire()方法获得这个锁,该线程就会被阻塞,直到拥有锁的线程调用锁的release()方法释放该锁,使其处于非锁定状态
  • 好处

    • 确保了某段关键代码只能由一个线程从头到尾完整地执行
  • 坏处

    • 阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大下降
    • 可能会造成死锁

二、Lock和RLock的主要区别

  • 主要区别

    • 一、名称上的区别:
      • Lock(原始锁)、RLock(重入锁)
    • 二、Lock在锁定时不属于任何线程,即允许在一个线程中上锁,另一个线程解锁,而RLock必须由同一个线程完成上锁/解锁过程
      import threading
      
      lock = threading.Lock()
      lock.acquire()
      
      def test():
          lock.release()
          print("lock is released")
      
      t = threading.Thread(target=test)
      t.start()

      能够输出结果“lock is released”,如果换成RLock,则会报错:“RuntimeError: cannot release un-acquired lock”

    • 三、RLock允许在同一线程中被多次连续acquire,而不会出现死锁情况(注意acquire和release必须成对出现),而Lock不允许,否则会出现死锁
      from threading import Lock,RLock
      
      lock = Lock()
      rlock = RLock()
      
      # 可以执行
      with rlock:
          with rlock:
              print('RLock锁')
      
      with lock:
          # 会出现死锁
          with lock:
              print('Lock锁')

三、Condition

  • 简介

    • Condition被称为条件变量,除了提供与RLock类似的acquire和release方法外(内部通过RLock实现),还提供了wait和notify等方法
  • wait(timeout=None)

    • 该方法执行流程:
      • 首先会释放内部占用的锁,同时线程被挂起(进入堵塞状态),直到被notify() 或 notify_all() 唤醒或者超时(设置了timeout参数)
      • 一旦被唤醒或超时,该线程将尝试重新获取锁
    • 返回值
      • 正常被唤醒,返回值为True
      • 超时则返回False
  • notify(n=1)

    • 如果有被挂起的线程,则唤醒n个被挂起的线程
    • 如果没有线程被挂起,则该方法调用是一个空操作
    • 并不会释放内部占用的锁
  • notify_all()

    • 唤醒正在等待本条件变量的所有线程
  • 示例(生产者消费者模式)

    import threading, time
    from random import randint
    
    
    class Producer(threading.Thread):
        def run(self):
            global L
            while True:
                val = randint(0, 100)
                with lock_con:
                    L.append(val)
                    print(f"生产者:{self.name}, Append:{val}, L = {L}")
                    lock_con.notify()
                time.sleep(3)
    
    
    class Consumer(threading.Thread):
        def run(self):
            global L
            while True:
                with lock_con:
                    if len(L) == 0:
                        print("队列为空,请等待。。。")
                        lock_con.wait()
                    print('>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>')
                    print(f"消费者: {self.name}, Delete: {L[0]}")
                    del L[0]
                time.sleep(0.5)
    
    
    if __name__ == '__main__':
        L = []  # 消费队列
        lock_con = threading.Condition()
        threads = []
        # 若干个生产者线程
        for i in range(3):
            threads.append(Producer())
        threads.append(Consumer())
        for t in threads:
            t.start()
        for t in threads:
            t.join()
posted @ 2023-01-15 07:49  eliwang  阅读(444)  评论(0编辑  收藏  举报