• GIL、Event事件、信号量、死锁、递归锁


    去幕布 >>

    GIL全局解释器锁

    Python解释器有很多种,最常见的就是CPython解释器
    GIL本质也是一把互斥锁:将并发变成串行,牺牲效率保证数据的安全​
    用来阻止同一个进程下的 多个线程的 同时执行
    (同一个进程内的多个线程无法实现并行,但是可以实现并发)

    Python的多线程没法利用多核优势,是不是就没有用了?

    GIL的存在是因为CPython解释器的内存管理不是线程安全的
    
    垃圾回收机制:
    a. 引用计数
    b. 标记清除
    c. 分代回收

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

    (假设有四个任务,每个任务需要10s 处理完成)​
    
    1. 计算密集型​:
    单核情况下​:
    ​开线程更省资源(原因是开进程需要申请内存空间等操作,浪费时间)
    多核情况下:
    开进程处理    10s
    开线程处理    40s
    
    
    2. I/O密集型:
    ​​​​​单核情况下:
    开线程更省资源(原因和计算密集型的一样)
    多核情况下:
    ​​​开线程更省资源
    ​(原因是 I/O秘籍情况下,线程工作方式为 切换+保存
    ​进程申请内存空间,是要消耗很长时间的,
    而​线程的切换+保存基本不消耗时间)
    ​
    
    python的多线程到底有没有用
    需要看情况而定  并且肯定是有用的
    多进程+多线程配合使用​
    View Code
    # I/O密集型
    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())  # 本机为8核
        start=time.time()
    ​
        for i in range(400):
            p=Process(target=work)  # 耗时9.506524085998535s多,大部分时间耗费在创建进程上
            # p=Thread(target=work)  # 耗时 2.0459132194519043 s多
            l.append(p)
            p.start()
    ​
        for p in l:
            p.join()
    ​
        stop=time.time()
        print('run time is %s' %(stop-start))
    I/O密集型
    # 计算密集型
    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())  # 本机为8核
        start=time.time()
    ​
        for i in range(8):
            p=Process(target=work)  # 耗时  8.460614204406738s
            # p=Thread(target=work)  # 耗时 34.822699308395386s
            l.append(p)
            p.start()
    ​
        for p in l:
            p.join()
    ​
        stop=time.time()
        print('run time is %s' %(stop-start))
    计算密集型

    死锁

    1. 代码表示下一步要acquire一个锁
    2. ​但是两个线程互相拿着对方需要的锁,于是程序就hang到这儿了
    这种情况就是死锁
    from threading import Thread, Lock, current_thread
    import time
    ​
    mutexA = Lock()
    mutexB = Lock()​​
    ​
    class MyThread(Thread):
    ​
        # 创建线程自动触发run方法 run方法内调用func1 func2相当于也是自动触发
        def run(self):  
            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锁
    ​(程序到这儿并没有结束,而是一直hang着...)
    ​
    ​​​'''
    死锁代码示例

     递归锁

    Rlock可以被第一个抢到锁的人连续的acquire和release
    每acquire一次锁身上的计数加1
    每release一次锁身上的计数减1
    只要锁的计数不为0 其他人都不能抢
    from threading import Thread, Lock, current_thread, RLock
    import time
    ​
    # mutexA = Lock()
    # mutexB = Lock()​​
    mutexA = mutexB = RLock()  # A B现在是同一把锁​
    class MyThread(Thread):
    ​
        # 创建线程自动触发run方法 run方法内调用func1 func2相当于也是自动触发
        def run(self):  
            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(2):
        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锁
    Thread-2抢到了B锁
    Thread-2抢到了A锁
    Thread-2释放了A锁
    Thread-2释放了B锁
    ​​'''
    递归锁代码示例

    Event事件

    前言
    三两赛车准备出发,但是要等信号灯打出绿灯信号时,才出发
    
    Event事件
    事件处理的机制。
    全局定义了一个内置标志Flag,
    Flag默认值为 False,那么当程序执行 event.wait方法时就会阻塞,
    如果Flag值为True,那么event.wait 方法时便不再阻塞。
    
    涉及的方法:
    set(): 将标志设为True,并通知所有处于等待阻塞状态的线程恢复运行状态。
    clear(): 将标志设为False。
    wait(timeout): 如果标志为True将立即返回,否则阻塞线程至等待阻塞状态,等待其他线程调用set()。
    isSet(): 获取内置标志状态,返回True或False。
    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(3):
        t = Thread(target=car,args=('伞兵%s'%i,))
        t.start()
    ​
    '''执行结果:
    红灯正亮着
    伞兵0正在等红灯
    伞兵1正在等红灯
    伞兵2正在等红灯
    (此处hang3s,因为sleep了3s)​
    绿灯亮了
    伞兵1加油门飙车了
    伞兵0加油门飙车了
    伞兵2加油门飙车了
    ​'''
    重现 前言 的情景

     信号量

    信号量semaphore
    是一个变量,控制着对公共资源或者临界区的访问。
    ​信号量维护着一个计数器,指定可同时访问资源或者进入临界区的线程数。
    
    实现机制:​​
    每次有一个线程获得信号量时,计数器-1。
    ​若计数器为0,其他线程就停止访问信号量,直到另一个线程释放信号量。
    # 信号量代码示例
    from threading import Semaphore,Thread
    import time
    import random
    ​
    sm = Semaphore(5)  # 造了一个含有五个的坑位的公共厕所
    def task(name):
        sm.acquire()
        print('%s占了一个坑位'%name)
        time.sleep(random.random())
        sm.release()
        print('%s释放了一个坑位' % name)
    ​
    for i in range(4):
        t = Thread(target=task,args=(i,))
        t.start()
    ​
    '''执行结果:
    0占了一个坑位
    1占了一个坑位
    2占了一个坑位
    3占了一个坑位
    1释放了一个坑位
    0释放了一个坑位
    2释放了一个坑位
    3释放了一个坑位
    ​'''​​
    信号量代码示例

    补充讲解三种队列

    同一个进程下的多个线程本来就是数据共享 为什么还要用队列
    因为队列是管道+锁  使用队列你就不需要自己手动操作锁的问题 
    因为锁操作的不好极容易产生死锁现象
    import Queue
    ​
    ​q = queue.Queue()
    q.put('hahha')
    print(q.get())
    # 执行结果:hahha
    ​​
    q = queue.LifoQueue()
    q.put(1)
    q.put(2)
    q.put(3)
    print(q.get())
    # 执行结果:3​
    ​​
    q = queue.PriorityQueue()
    # 数字越小 优先级越高
    q.put((10,'haha'))
    q.put((100,'hehehe'))
    q.put((0,'xxxx'))
    q.put((-10,'yyyy'))
    print(q.get())
    # 执行结果:(-10, 'yyyy')​
    Queue、LiFoQueue、Priority
  • 相关阅读:
    java项目数据库从oracle迁移到mysql 中 java部分的一些修改
    mysql表名等大小写敏感问题、字段类型timestamp、批量修改表名、oracle查询历史操作记录等
    navicat premium相关应用(将oracle数据库迁移到mysql等)
    Java byte 类型的取值范围是-128~127
    idea中debug:
    chrome里面模拟手机上打开网页的场景方法
    Dealloc weak nil
    用七牛sdk传递图片到七牛服务器
    iOS block 本质研究
    UIWebView JSContext相关问题
  • 原文地址:https://www.cnblogs.com/buzaiyicheng/p/11352588.html
Copyright © 2020-2023  润新知