美文网首页
38.1-Lock锁

38.1-Lock锁

作者: BeautifulSoulpy | 来源:发表于2020-01-28 09:17 被阅读0次

    趁你现在还有时间,尽你自己最大的努力,努力做成你最想做的那件事,成为你最想成为的那种人,过着你最想过的那种生活!

    image

    总结:

    1. 锁效率低,但是结果正确;

    1. Lock ***

    当多个进程需要访问共享资源的时候,Lock可以用来避免访问的冲突。思考一个场景:无事务状态下共同访问某个内存变量或者多个进程要访问读写同一个文件。

    ,凡是存在共享资源争抢的地方都可以使用锁,从而保证只有一个使用者可以完全使用这个资源;

    Lock的使用主要有以下几个方法:
    mutex = threading.Lock() # 创建 互斥锁
    mutex.acquire([timeout]) # 锁定,其他人无法访问;
    mutex.release() # 释放

    需求:订单要求生产1000个杯子,组织10个工人生产

    import threading
    from threading import Thread, Lock
    import logging
    import time
    
    FORMAT = '%(asctime)s %(threadName)s %(thread)d %(message)s'
    logging.basicConfig(format=FORMAT, level=logging.INFO)
    cups = []
    
    def worker(count=10):
        logging.info("I'm working for U.")
        while len(cups) < count:
            time.sleep(0.0001)  # 为了看出线程切换效果
            cups.append(1)
        logging.info('I finished. cups = {}'.format(len(cups)))
    
    for i in range(10):
        Thread(target=worker, args=(1000,),name='work-{}'.format(i)).start()
    #----------------------------------------------------------------------------------------------
    2020-01-14 09:09:02,520 Thread-8 17852 I finished. cups = 1000
    2020-01-14 09:09:02,520 Thread-2 20600 I finished. cups = 1001
    2020-01-14 09:09:02,520 Thread-5 22464 I finished. cups = 1002
    2020-01-14 09:09:02,520 Thread-10 21448 I finished. cups = 1003
    2020-01-14 09:09:02,520 Thread-1 164 I finished. cups = 1004
    2020-01-14 09:09:02,520 Thread-3 7648 I finished. cups = 1005
    2020-01-14 09:09:02,521 Thread-7 20308 I finished. cups = 1006
    2020-01-14 09:09:02,522 Thread-4 16744 I finished. cups = 1007
    2020-01-14 09:09:02,522 Thread-6 18224 I finished. cups = 1008
    2020-01-14 09:09:02,522 Thread-9 21656 I finished. cups = 1009
    

    从上例的运行结果看出,多线程调度,导致了判断失效,多生产了杯子。如何修改?加锁;

    import threading
    from threading import Thread, Lock
    import logging
    import time
    
    FORMAT = '%(asctime)s %(threadName)s %(thread)d %(message)s'
    logging.basicConfig(format=FORMAT, level=logging.INFO)
    cups = []
    lock = Lock()
    
    def worker(total=100):
        logging.info("I'm working")
        flag = False
    
        while True:
            lock.acquire() # 获得锁;
            if len(cups) >= total:
                flag = True
            #lock.release()   # 无法阻止线程生产;
    
            if not flag:
                time.sleep(0.0001)  # 为了看出线程切换效果
                cups.append(1)
            lock.release()
    
            if flag:
                break
            #ock.release()   # 范围扩大,锁未释放;
        logging.info('I finished. cups = {}'.format(len(cups)))
    
    total = 1000
    for i in range(10):
        Thread(target=worker, args=(total,),name='work-{}'.format(i)).start()
    #----------------------------------------------------------------------------------------------
    2020-01-14 09:49:04,930 work-0 19088 I'm working
    2020-01-14 09:49:04,930 work-1 22352 I'm working
    2020-01-14 09:49:04,930 work-2 19536 I'm working
    2020-01-14 09:49:04,931 work-3 12828 I'm working
    2020-01-14 09:49:04,931 work-4 10168 I'm working
    2020-01-14 09:49:04,931 work-5 14740 I'm working
    2020-01-14 09:49:04,931 work-6 14528 I'm working
    2020-01-14 09:49:04,931 work-7 22824 I'm working
    2020-01-14 09:49:04,931 work-8 19720 I'm working
    2020-01-14 09:49:04,931 work-9 21016 I'm working
    2020-01-14 09:49:06,916 work-8 19720 I finished. cups = 1000
    2020-01-14 09:49:06,916 work-1 22352 I finished. cups = 1000
    2020-01-14 09:49:06,916 work-3 12828 I finished. cups = 1000
    2020-01-14 09:49:06,916 work-4 10168 I finished. cups = 1000
    2020-01-14 09:49:06,916 work-5 14740 I finished. cups = 1000
    2020-01-14 09:49:06,916 work-6 14528 I finished. cups = 1000
    2020-01-14 09:49:06,916 work-7 22824 I finished. cups = 1000
    2020-01-14 09:49:06,916 work-0 19088 I finished. cups = 1000
    2020-01-14 09:49:06,916 work-9 21016 I finished. cups = 1000
    2020-01-14 09:49:06,916 work-2 19536 I finished. cups = 1000
    

    Lock
    锁,一旦线程获得锁,其它试图获取锁的线程将被阻塞

    名称 含义
    acquire(blocking=True,timeout=-1) 默认阻塞,阻塞可以设置超时时间。非阻塞时,timeout禁止设置。成功获取锁,返回True,否则返回False
    release() 释放锁。可以从任何线程调用释放。已上锁的锁,会被重置为unlocked未上锁的锁上调用,抛RuntimeError异常。
    import threading
    from threading import Thread, Lock
    import time
    
    class Counter:
        def __init__(self):
            self._val = 0
    
        @property
        def value(self):
            return self._val
        def inc(self):
            self._val += 1
        def dec(self):
            self._val -= 1
    
    def run(c:Counter, count=100):
        for _ in range(count):
            for i in range(-50,50):
                if i < 0:
                    c.dec()
                else:
                    c.inc()
    
    c = Counter()
    c1 = 10 # 线程数
    c2 = 10
    for i in range(c1):
        Thread(target=run,args=(c,c2)).start()
    print(c.value)
    #------------------------------------------------------------------
    39
    

    c1取10、100、1000看看
    c2取10、100、1000看看
    self._val += 1 或 self._val -= 1 在线程中执行的时候,有可能被打断。
    要加锁。怎么加?

    加锁、解锁

    一般来说,加锁就需要解锁,但是加锁后解锁前,还要有一些代码执行,就有可能抛异常,一旦出现异常,锁是无
    法释放,但是当前线程可能因为这个异常被终止了,这就产生了死锁。

    加锁、解锁常用语句:
    1、使用try...finally语句保证锁的释放
    2、with上下文管理,锁对象支持上下文管理
    改造Couter类,如下

    import threading
    from threading import Thread, Lock
    import time
    
    class Counter:
        def __init__(self):
            self._val = 0
            self.__lock = Lock()
    
        @property
        def value(self):
            with self.__lock:
                return self._val
        def inc(self):
            try:
                self.__lock.acquire()
                self._val += 1
            finally:
                self.__lock.release()
        def dec(self):
            with self.__lock:
                self._val -= 1
    
    def run(c:Counter, count=100):
        for _ in range(count):
            for i in range(-50,50):
                if i < 0:
                    c.dec()
                else:
                    c.inc()
    
    c = Counter()
    c1 = 10 # 线程数
    c2 = 10
    
    while True:
        time.sleep(1)
        if threading.active_count() == 1:
            print(threading.enumerate())
            print(c.value)
            break
        else:
            print(threading.enumerate())
    #------------------------------------------------------
    
    

    print(c.value) 这一句在主线程中,很早就执行了。退出条件是,只剩下主线程的时候。
    这样的改造后,代码可以保证最后得到的value值一定是0。

    锁的应用场景

    锁适用于访问和修改同一个共享资源的时候,即读写同一个资源的时候。

    如果全部都是读取同一个共享资源需要锁吗?
    不需要。因为这时可以认为共享资源是不可变的,每一次读取它都是一样的值,所以不用加锁

    使用锁的注意事项:

    1. 少用锁,必要时用锁。使用了锁,多线程访问被锁的资源时,就成了串行,要么排队执行,要么争抢执行;
      举例,高速公路上车并行跑,可是到了省界只开放了一个收费口,过了这个口,车辆依然可以在多车道
      上一起跑。过收费口的时候,如果排队一辆辆过,加不加锁一样效率相当,但是一旦出现争抢,就必须
      加锁一辆辆过。
    2. 加锁时间越短越好,不需要就立即释放锁
    3. 一定要避免死锁
      不使用锁,有了效率,但是结果是错的。
      使用了锁,效率低下,但是结果是对的。
      所以,我们是为了效率

    相关文章

      网友评论

          本文标题:38.1-Lock锁

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