zoukankan      html  css  js  c++  java
  • GIL全局解释器锁/Event事件/信号量/线程Queue

    GLL全局解释器锁

    背景:python解释器有很多种,最常见的就是Cpython解释器

    定义:GIL本质也是一把互斥锁:将并发变成串行,牺牲效率保证数据的安全;用来阻止同一个进程下的多个线程同时执行(同一个进程内多个线程无法实现并行,但是可以实现并发)

    注意:python的多线程没法利用多核优势,并不是没用了

    GIL的作用与意义:

    • GIL的存在是因为Cpython解释器的内存管理是不安全的
    • GIL是Cpython解释器的特点,且单进程下多个线程无法利用多核优势是所有解释器语言的通病
    • 针对不同数据应该加不同的锁进行处理
    • GIL用来保证线程安全

    研究python的多线程是否有用需要分情况讨论

    • 若有四个任务,且是计算密集型
      • 单核情况下
        • 开线程更省资源
      • 多核情况下
        • 开进程时间少点
        • 开线程时间长点
    # 计算密集型
    from multiprocessing import Process
    from threading import Thread
    import os,time
    ​
    def work():
         res=0
         for i in range(100000000):
            res*=i
    ​
    if __name__ == '__main__':
         l=[]
         print(os.cpu_count())  # 本机为6核
         start=time.time()
         for i in range(6):
              p=Process(target=work) # 耗时  4.732933044433594
             p=Thread(target=work) # 耗时 22.83087730407715
             l.append(p)
             p.start()
         for p in l:
             p.join()
         stop=time.time()
         print('run time is %s' %(stop-start))

    若有四个任务,且是IO密集型

    • 单核情况下
      • 开线程更节省资源
    • 多核情况下
      • 开线程更节省资源
    # IO密集型
    from multiprocessing import Process
    from threading import Thread
    import threading
    import os,time
    def work():
        time.sleep(2)
    ​
    if __name__ == '__main__':
        l=[]
        print(os.cpu_count()) #本机为6核
        start=time.time()
        for i in range(4000):
            p=Process(target=work) # 耗时9.001083612442017s多,大部分时间耗费在创建进程上
            # p=Thread(target=work) # 耗时2.051966667175293s多
            l.append(p)
            p.start()
        for p in l:
            p.join()
        stop=time.time()
        print('run time is %s' %(stop-start))

    总结:

    • python的多线程到底有没有用,需要看情况而定,首先可以肯定是有用的
    • 多进程+多线程配合使用

    GIL与普通的互斥锁

    from threading import Thread
    import time
    ​
    n = 100def task():
        global n
        tmp = n
        time.sleep(1)
        n = tmp -1
    ​
    t_list = []
    for i in range(100):
        t = Thread(target=task)
        t.start()
        t_list.append(t)
    ​
    for t in t_list:
        t.join()
    ​
    print(n)  # 99

    死锁

    PS:只要类加括号实例化对象,无论传入的参数是否一样,生成的对象肯定不一样,单例模式除外

    注意:自己千万不要轻易的处理锁的问题

    from threading import Thread,Lock
    import time
    ​
    mutexA = Lock()
    mutexB = Lock()
    ​
    class MyThread(Thread):
        def run(self):  # 创建线程自动触发run方法,方法内调用func1,func2也相当于是自动触发
            self.func1()
            self.func2()
        
        def func1(self):
            mutexA.acquire()
            print('%s抢到了A锁'%self.name)  # self.name等价于current_thread().name
            mutexB.acquire()
            print('%s抢到了B锁'%self.name)
            mutexB.release()
            print('%s释放了B锁'%self.name)
            mutexA.release()
            print('%s释放了A锁'%self.name)
        
        def func2(self):
            mutexB.acquire()
            print('%s抢到了B锁'%self.name)
            time.sleep(1)
            mutexA.acquire()
            print('%s抢到了A锁'%self.name)
            mutexA.release()
            print('%s释放了A锁'%self.name)
            mutexB.release()
            print('%s释放了B锁'%self.name)
            
    for i in range(10):
        t = MyThread()
        t.start()
    '''
    Thread-1抢到了A锁
    Thread-1抢到了B锁
    Thread-1释放了B锁
    Thread-1释放了A锁
    Thread-1抢到了B锁
    Thread-2抢到了A锁
    '''

    RLock

    定义:RLock是递归锁

    • 可以被第一个抢到锁的人连续的acquire和release
    • 每acquire一次锁身上的计数加1
    • 每release一次锁身上的计数加1
    • 只要锁的计数不为0,其他人都不能抢
    from threading import Thread,RLock
    import time
    ​
    mutexA = mutexB = RLock()
    ​
    class MyThread(Thread):
        def run(self):  # 创建线程自动触发run方法,方法内调用func1,func2也相当于是自动触发
            self.func1()
            self.func2()
        
        def func1(self):
            mutexA.acquire()
            print('%s抢到了A锁'%self.name)  # self.name等价于current_thread().name
            mutexB.acquire()
            print('%s抢到了B锁'%self.name)
            mutexB.release()
            print('%s释放了B锁'%self.name)
            mutexA.release()
            print('%s释放了A锁'%self.name)
        
        def func2(self):
            mutexB.acquire()
            print('%s抢到了B锁'%self.name)
            time.sleep(1)
            mutexA.acquire()
            print('%s抢到了A锁'%self.name)
            mutexA.release()
            print('%s释放了A锁'%self.name)
            mutexB.release()
            print('%s释放了B锁'%self.name)
            
    for i in range(10):
        t = MyThread()
        t.start()
    '''
    Thread-1抢到了A锁
    Thread-1抢到了B锁
    Thread-1释放了B锁
    Thread-1释放了A锁
    Thread-1抢到了B锁
    Thread-1抢到了A锁
    Thread-1释放了A锁
    Thread-1释放了B锁
    Thread-2抢到了A锁
    Thread-2抢到了B锁
    Thread-2释放了B锁
    Thread-2释放了A锁
    ...
    '''

    Event事件

    from threading import Event,Thread
    import time
    ​
    # 先生成一个event对象
    e = Event()
    ​
    def light():
        print('红灯正亮着')
        time.sleep(3)
        e.set()  # 发信号
        print('绿灯亮了')
    ​
    def car(name):
        print('%s正在等红灯'%name)
        e.wait()  # 等待信号
        print('%s加油门飙车了'%name)
    ​
    t = Thread(target=light)
    t.start()
    ​
    for i in range(6):
        t = Thread(target=car,args=('伞兵%s'%i,))
        t.start()
    '''
    红灯正亮着
    伞兵0正在等红灯
    伞兵1正在等红灯
    伞兵2正在等红灯
    伞兵3正在等红灯
    伞兵4正在等红灯
    伞兵5正在等红灯
    (三秒后)

    绿灯亮了
    伞兵4加油门飙车了
    伞兵5加油门飙车了
    伞兵2加油门飙车了
    伞兵3加油门飙车了
    伞兵0加油门飙车了
    伞兵1加油门飙车了
    '''

    信号量

    • 互斥锁类似一个厕所(一个坑位)
    • 信号量类似公共厕所(多个坑位)
    from threading import Semaphore,Thread
    import time
    import random
    ​
    sm = Semaphore(5)  # 造了一个有5个坑位的公共厕所
    def task(name):
        sm.acquire()
        print('%s占了一个坑位'%name)
        time.sleep(random.randint(1,3))
        sm.release()
        
    for i in range(10):
        t = Thread(target=task,args=(i,))
        t.start()
    '''
    0占了一个坑位
    1占了一个坑位
    2占了一个坑位
    3占了一个坑位
    4占了一个坑位
    5占了一个坑位
    7占了一个坑位
    6占了一个坑位
    8占了一个坑位
    9占了一个坑位
    '''

    线程Queue

    同一个进程下的多个线程本来就是数据共享,为什么还要用队列?

    • 因为队列是管道+锁 使用队列你就不需要自己手动操作锁的问题
    • 因为锁操作不当极容易产生死锁现象
    import queue
    ​
    q = queue.Queue()
    q.put('hahha')
    print(q.get())
    ​
    q = queue.LifoQueue()
    q.put(1)
    q.put(2)
    q.put(3)
    print(q.get())
    ​
    q = queue.PriorityQueue()
    # 数字越小 优先级越高
    q.put((10,'haha'))
    q.put((100,'hehehe'))
    q.put((0,'xxxx'))
    q.put((-10,'yyyy'))
    print(q.get())
    '''
    hahha
    3
    (-10, 'yyyy')
    '''
  • 相关阅读:
    Cookie工具类
    验证工具类
    压缩工具类
    一次外企QQ面试
    利用Referer请求头阻止"盗链"
    servlet中ServletConfig的使用
    jquery插件制作
    jQuery选择器总结(转)
    js文件加载执行顺序
    mysql有关问题之:the security settings could not be applied to
  • 原文地址:https://www.cnblogs.com/zhukaijian/p/11352370.html
Copyright © 2011-2022 走看看