zoukankan      html  css  js  c++  java
  • 为什么有这把GIL锁?

      答:Python语言的创始人在开发这门语言时,目的快速把语言开发出来,如果加上GIL锁(C语言加锁),切换时按照100条字节指令来进行线程间的切换。

    一.线程锁(Lock、RLock) (1次放1个)

      由于线程之间是进行随机调度,并且每个线程可能只执行n条执行之后,当多个线程同时修改同一条数据时可能会出现脏数据,所以,出现了线程锁 - 同一时刻允许一个线程执行操作。

      1.Lock (同步锁)  没有加锁

    import threading
    import time
    v = []
    lock = threading.Lock()
    def func(arg):
        v.append(arg)
        time.sleep(2)
        m = v[-1]
        print(arg,m)
    for i in range(10):
        t = threading.Thread(target=func,args=(i,))
        t.start()
    #结果:0 9
       1 9
       4 9
       3 9
       2 9
       6 9
       5 9
       9 9
       8 9
       7 9
    import threading
    import time
    v = []
    lock = threading.Lock()
    def func(arg):
        lock.acquire()
        v.append(arg)
        time.sleep(2)
        m = v[-1]
        print(arg,m)
        lock.release()
    for i in range(10):
        t = threading.Thread(target=func,args=(i,))
        t.start()
    #结果:0 0
          1 1
          2 2
          3 3
          4 4
          5 5
          6 6
          7 7
          8 8
          9 9   
    加锁

      死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程,如下就是死锁

     死锁

    `  解决方法,递归锁,在Python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。

    这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。上面的例子如果使用RLock代替Lock,则不会发生死锁:

      2.RLock(一次放一个)( 递归锁 )

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

      3.BoundedSemaphore(1次放N个)信号量

    互斥锁 同时只允许一个线程更改数据,而Semaphore是同时允许一定数量的线程更改数据 ,比如厕所有3个坑,那最多只允许3个人上厕所,后面的人只能等里面有人出来了才能再进去。

    Semaphore管理一个内置的计数器,
    每当调用acquire()时内置计数器-1;
    调用release() 时内置计数器+1;
    计数器不能小于0;当计数器为0时,acquire()将阻塞线程直到其他线程调用release()。

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

      4.条件(Condition)(1次放动态N个)

    使得线程等待,只有满足某条件时,才释放n个线程

    import threading
    import time
    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()
    import threading
    import time
    lock = threading.Condition()
    def func1():
        print('来执行函数了')
        input(">>>")
        # ct = threading.current_thread() # 获取当前线程
        # ct.getName()
        return True
    def func(arg):
        print('线程进来了')
        lock.wait_for(func1)
        print(arg)
        time.sleep(1)
    for i in range(10):
        t =threading.Thread(target=func,args=(i,))
        t.start()
    方式二

      5.事件Event(1次放所有)

    python线程的事件用于主线程控制其他线程的执行,事件主要提供了三个方法 set、wait、clear。

    事件处理的机制:全局定义了一个“Flag”,如果“Flag”值为 False,那么当程序执行 event.wait 方法时就会阻塞,如果“Flag”值为True,那么event.wait 方法时便不再阻塞。

    • clear:将“Flag”设置为False
    • set:将“Flag”设置为True
    event.isSet():返回event的状态值;
    event.wait():如果 event.isSet()==False将阻塞线程;
    event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度;
    event.clear():恢复event的状态值为False。
    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()

    总结:

      线程安全,列表和字典是线程安全

      为什么要加锁?

         非线程安全

        可以控制一段代码

    二.threading.local

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

    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()

      threading.local的原理

    import time
    import threading
    DATA_DICT = {}
    def func(arg):
        ident = threading.get_ident()
        DATA_DICT[ident] = arg
        time.sleep(1)
        print(DATA_DICT[ident],arg)
    for i in range(10):
        t =threading.Thread(target=func,args=(i,))
        t.start()
    View Code

    三.线程池

    进程池:就是在一个进程内控制一定个数的线程
    基于concurent.future模块的进程池和线程池 (他们的同步执行和异步执行是一样的)
    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 threading
    import time
    def task(arg):
        time.sleep(10)
    while True:
        num = input(">>>")
        t = threading.Thread(target=task,args=(num,))
        t.start()
    线程
    import time
    from concurrent.futures import ThreadPoolExecutor
    def task(arg):
        time.sleep(5)
    pool = ThreadPoolExecutor(2)
    while True:
        num = input(">>>")
        pool.submit(task,num)
    线程池

    四.生产者消费者模型

    1.三部件:

        生产者      队列:先进先出   (栈:后进先出)       消费者

    2.线程队列

      queue队列 :使用import queue,用法与进程Queue一样

      class queue.Queue(maxsize=0) #先进先出
    import queue
    
    q=queue.Queue()
    q.put('first')
    q.put('second')
    q.put('third')
    
    print(q.get())
    print(q.get())
    print(q.get())
    '''
    结果(先进先出):
    first
    second
    third
    '''
    先进先出

      class queue.LifoQueue(maxsize=0) #last in fisrt out

    import queue
    
    q=queue.LifoQueue()
    q.put('first')
    q.put('second')
    q.put('third')
    
    print(q.get())
    print(q.get())
    print(q.get())
    '''
    结果(后进先出):
    third
    second
    first
    '''
    后进先出

      class queue.PriorityQueue(maxsize=0) #存储数据时可设置优先级的队列

    import queue
    
    q=queue.PriorityQueue()
    #put进入一个元组,元组的第一个元素是优先级(通常是数字,也可以是非数字之间的比较),数字越小优先级越高
    q.put((20,'a'))
    q.put((10,'b'))
    q.put((30,'c'))
    
    print(q.get())
    print(q.get())
    print(q.get())
    '''
    结果(数字越小优先级越高,优先级高的优先出队):
    (10, 'b')
    (20, 'a')
    (30, 'c')
    '''
    优先级队列

    3.生产者消费者模型解决了什么问题?

      可以不用一直等待的问题

    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()
  • 相关阅读:
    SpringCloud学习(二)---Eureka
    【Jmeter源码解读】001——目录结构
    TCP连接可能出现的异常总结
    TCP的socket连接
    soap-ws获取ws中的所有的接口方法
    webservice的hello world
    【环境搭建】Angular (含Hello World)
    使用Spring-boot-admin对Spring boot的服务进行监控
    idea的配置文件------application.properties和application.yml
    Spring Boot常用的注解以及含义<持续更新>
  • 原文地址:https://www.cnblogs.com/chenxi67/p/9627797.html
Copyright © 2011-2022 走看看