美文网首页
Python中的线程锁

Python中的线程锁

作者: welder77 | 来源:发表于2022-08-13 22:31 被阅读0次

    今天写一下工作中遇到的问题线程锁,其实过去对何时应该用线程锁有些模糊。所以工作中保险起见会在所有线程的变量修改处都加上锁,以起到保护作用。

    其实这样做既盲目又增加了工作量,今天我们一同研究下线程锁的作用和应用场景。

    我们还是通过一个小例子来说明:
    事情是这样的,我们平时保管着2个CANoe,一个VN5640,一个VN1640,有些时候VN1640是闲置的,因为我们有了国货优品Tsmaster了,所以我们就开发一套在线CANoe租借系统,在我们CANoe有闲置的时候,外借给其他同事,提高CANoe的使用率。

    下面的租借系统代码经过简单调试就上线了:

    import time
    import threading
    
    #CANoe账户,用于存储管理者和余量
    class CANoe_Amount():
        def __init__(self, account_name, CANoe):
        # 构造函数account_name管理账户名,CANoe为剩余数量
            self.account_name=account_name
            self.CANoe=CANoe
    
    #借用函数
    def borrow(account,draw_amount):
        if account.CANoe >= draw_amount:
            print(threading.current_thread().name+'借用成功,取出:'+str(draw_amount))
            time.sleep(1)
            account.CANoe -= draw_amount
            print('剩余CANoe为:'+str(account.CANoe))
        else:
            print(f"{threading.current_thread().name}想借用,但{str(account.account_name)}名下的CANoe余量不足")
    
    CANoe=CANoe_Amount('gm',1)  #实例化CANoe类并挂出了一个CANoe余量
    threading.Thread(name="XX",target=borrow,args=(CANoe,1)).start()  #线程1 XX想借用一个
    threading.Thread(name="YY",target=borrow,args=(CANoe,1)).start()  #线程2 YY想借用一个
    

    一天VN1640闲置下来了,我们就在线挂出了1个CANoe的外借余量。 Wz和Ww这两个小伙伴,同时看到了信息,并同时在线申请了借用。

    threading.Thread(name="Wz",target=borrow,args=(CANoe,1)).start()  #线程1 Wz想借用一个
    threading.Thread(name="Ww",target=borrow,args=(CANoe,1)).start()  #线程2 Ww想借用一个
    

    结果你猜怎么了,系统提示如下信息,他俩都外借成功了这个CANoe。

    Wz借用成功,取出:1
    Ww借用成功,取出:1
    剩余CANoe为:0 剩余CANoe为:-1
    

    不是只挂了一个的余量,为什么两个人都借到了哪?其实问题就出在线程上,我们把Wz和Ww的申请比作两个线程,他们几乎同一时间申请获取了同一个变量的值和修改权,两个线程对同一变量来说是互相干扰的。

    这样的结果,显然是违背了当初的设想的,于是我们请出了线程锁,修改后的代码如下:

    import time
    import threading
    class CANoe_Amount():
        def __init__(self,account_name, CANoe):
        # 构造函数account_name账户名,CANoe为剩余数量
            self.account_name=account_name
            self.CANoe=CANoe
    
    #借用函数
    def borrow(account,draw_amount):
        with lock:  #通过上下文添加线程锁
            if account.CANoe >= draw_amount:
                print(threading.current_thread().name+'借用成功,取出:'+str(draw_amount))
                time.sleep(1)
                account.CANoe -= draw_amount
                print('剩余CANoe为:'+str(account.CANoe))
            else:
                print(f"{threading.current_thread().name}想借用,但{str(account.account_name)}名下CANoe余量不足")
    
    a=CANoe_Amount('Gm',1)
    lock = threading.Lock()
    threading.Thread(name="Wz",target=borrow,args=(a,1)).start()
    threading.Thread(name="Ww",target=borrow,args=(a,1)).start()
    

    使用了线程锁后,运行结果如下:

    Wz借用成功,取出:1
    剩余CANoe为:0
    Ww想借用,但Gm名下的CANoe余量不足
    

    这样的结果才是符合我们预期的,同时我们发现,加了锁以后,线程有了执行的先后,在Wz线程执行的过程中(包含1s的等待),Ww线程是完全处于阻塞等待的状态。只有Wz执行完之后,锁被释放时,Ww线程才能被执行。

    总结,通过这个例子:

    • 我们了解了线程锁的作用,即保证数据的一致性 ,对锁内的资源(变量)进行锁定,避免其它线程对其进行篡改。
    • 通常类似于以上例子中,同一函数建立多个不同线程的Case,都建议在修改共同变量时添加锁,以防止冲突。
    • 推荐使用with lock:的方式来添加锁,可以避免误操作带来的死锁,代码也比较简明。但不要嵌套使用,如果需要嵌套使用,需要换用RLOCK(LOCK的升级版)。
    • 锁的应用对程序的执行速度也带来了弊端,首先创建锁需要时间,其次锁定状态下,其他带锁的线程是阻塞状态。只有一个线程是在工作的。所以要避免在锁内使用不必要的等待。
    • 另外,有一点也需要注意,一个线程添加了锁,一个线程不添加,那么不添加锁的线程,是不会受到锁的限制的。

    相关文章

      网友评论

          本文标题:Python中的线程锁

          本文链接:https://www.haomeiwen.com/subject/jaccgrtx.html