• 锁, threading.local, 线程池, 生产者消费者模型


    一. 锁:Lock (1次放1个)

        线程安全,多线程操作时,内部会让所有线程排队处理。如:list/dict/Queue
        线程不安全 + 人 => 排队处理。

        需求:
            a. 创建100个线程,在列表中追加8
            b. 创建100个线程
                v = []
                锁
                - 把自己的添加到列表中。
                - 在读取列表的最后一个。
                解锁

        锁一个代码块:

    import threading
    import time
    v = []
    lock = threading.Lock()
    
    def func(arg):
        lock.acquire()
        v.append(arg)
        time.sleep(0.01)
        m = v[-1]
        print(arg,m)
        lock.release()
    
    for i in range(10):
        t =threading.Thread(target=func,args=(i,))
        t.start()
    

    锁:RLock (1次放1个) 

    如果是threading.Lock, 出现两次lock.acquare()就会直接锁死了

    import threading
    import time
    
    v = []
    lock = threading.RLock()
    def func(arg):
        lock.acquire()
        lock.acquire()
    
        v.append(arg)
        time.sleep(0.01)
        m = v[-1]
        print(arg,m)
    
        lock.release()
        lock.release()
    
    
    for i in range(10):
        t =threading.Thread(target=func,args=(i,))
        t.start()
    

    锁:BoundedSemaphore(1次放N个)信号量, 同一时间只能通过N个线程

    import time
    import threading
    
    lock = threading.BoundedSemaphore(3)
    def func(arg):
        lock.acquire()
        print(arg)
        time.sleep(1)
        lock.release()
    
    
    for i in range(20):
        t =threading.Thread(target=func,args=(i,))
        t.start()
    

    锁:Condition(1次放x个)

    import time
    import threading
    
    lock = threading.Condition()
    
    # ############## 方式一 ##############
    
    def func(arg):
        print('线程进来了')
        lock.acquire()
        lock.wait() # 加锁
    
        print(arg)
        time.sleep(1)
    
        lock.release()
    
    
    for i in range(10):
        t =threading.Thread(target=func,args=(i,))
        t.start()
    
    while True:
        inp = int(input('>>>'))
    
        lock.acquire()
        lock.notify(inp)
        lock.release()
    
    
    # ############## 方式二 ##############
    def xxxx():
        print('来执行函数了')
        input(">>>")
        # ct = threading.current_thread() # 获取当前线程
        # ct.getName()
        return True
    
    def func(arg):
        print('线程进来了')
        lock.wait_for(xxxx)
        print(arg)
        time.sleep(1)
    
    for i in range(10):
        t =threading.Thread(target=func,args=(i,))
        t.start()
    

    锁:Event(1次放所有)

    import time
    import threading
    
    lock = threading.Event()
    
    
    def func(arg):
        print('线程来了')
        lock.wait() # 加锁:红灯
        print(arg)
    
    
    for i in range(10):
        t =threading.Thread(target=func,args=(i,))
        t.start()
    
    input(">>>>")
    lock.set() # 绿灯
    
    
    lock.clear() # 再次变红灯
    
    for i in range(10):
        t =threading.Thread(target=func,args=(i,))
        t.start()
    
    input(">>>>")
    lock.set()
    

    总结:

        线程安全: 线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。 线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。

        通常加锁也有2种不同的粒度的锁:
        1. fine-grained(细粒度),程序员需要自行加/解锁来保证线程安全
        2. coarse-grained(粗粒度),语言层面本身维护着一个全局的锁机制用来保证线程安全
        前一种方式比较典型的是 Java, Jython 等, 后一种方式比较典型的是 CPython (即Python)。

        线程安全,列表和字典线程安全;
        加锁原因:
            - 非线程安全
            - 控制一段代码

    二. threading.local

        作用:
            内部自动为每个线程维护一个空间(字典),用于当前存取属于自己的值。保证线程之间的数据隔离。
            {
                线程ID: {...}
                线程ID: {...}
                线程ID: {...}
                线程ID: {...}
            }

        示例:

    import time
    import threading
    
    v = threading.local()
    
    def func(arg):
        # 内部会为当前线程创建一个空间用于存储:phone=自己的值
        v.phone = arg
        time.sleep(2)
        print(v.phone,arg) # 去当前线程自己空间取值
    
    for i in range(10):
        t =threading.Thread(target=func,args=(i,))
        t.start()
    

    三. 线程池

    from concurrent.futures import ThreadPoolExecutor
    import time
    
    def task(a1,a2):
        time.sleep(2)
        print(a1,a2)
    
    # 创建了一个线程池(最多5个线程)
    pool = ThreadPoolExecutor(5)
    
    for i in range(40):
        # 去线程池中申请一个线程,让线程执行task函数。
        pool.submit(task,i,8)
    

    四. 生产者消费者模型
        三部件:
            生产者
                队列,先进先出
                    扩展: 栈,后进先出
            消费者

        生产者消费者模型解决了不用一直等待的问题。

        示例:

    import time
    import queue
    import threading
    q = queue.Queue() # 线程安全
    
    def producer(id):
        """
        生产者
        :return:
        """
        while True:
            time.sleep(2)
            q.put('包子')
            print('厨师%s 生产了一个包子' %id )
    
    for i in range(1,4):
        t = threading.Thread(target=producer,args=(i,))
        t.start()
    
    
    def consumer(id):
        """
        消费者
        :return:
        """
        while True:
            time.sleep(1)
            v1 = q.get()
            print('顾客 %s 吃了一个包子' % id)
    
    for i in range(1,3):
        t = threading.Thread(target=consumer,args=(i,))
        t.start()
    
  • 相关阅读:
    LVS集群的ipvsadm命令用法
    opencv学习HighGUI图形用户界面初步【1】
    openc下cv::Mat和IplImage的相互转换
    QT的creator中图示
    android的数据与访问(2)-delphi xe7如何存取我的app配置参数文件?
    win7下qt+opencv的环境配置
    android的数据与访问(1)-我的app配置参数文件放在哪儿?
    xe7android调用webservice
    android.permission
    在win7下,easyphp安装过程中MSVCR110.DLL没有被指定在WINDOWS上运行,或者它包含错误
  • 原文地址:https://www.cnblogs.com/NachoLau/p/9630794.html
Copyright © 2020-2023  润新知