zoukankan      html  css  js  c++  java
  • 并发编程——全局解释器锁GIL

    1.全局解释器锁GIL

    • GIL其实就是一把互斥锁(牺牲了效率但是保证了数据的安全)。

    • 线程是执行单位,但是不能直接运行,需要先拿到python解释器解释之后才能被cpu执行

    • 同一时刻同一个进程内多个线程无法实现并行,但是可以实现并发

    • 为什么要有GIL是因为它内部的垃圾回收机制不是线程安全的

    • 垃圾回收机制也是一个任务,跟你的代码不是串行运行,如果是串行会明显有卡顿

    • 这个垃圾回收到底是开进程还是开线程?肯定是线程,线程肯定也是一段代码,所以想运行也必须要拿到python解释器

    • 假设能够并行,会出现什么情况?一个线程刚好要造一个a=1的绑定关系之前,这个垃圾线程来扫描,矛盾点就来了,谁成功都不对!

    • 也就意味着在Cpython解释器上有一把GIL全局解释器锁

    • 同一个进程下的多个线程不能实现并行但是能够实现并发,多个进程下的线程能够实现并行

    1.python中的多线程到底有没有用?

    单核情况下:四个任务

    多核情况下:四个任务

    计算密集型:一个任务算十秒,四个进程和四个线程,肯定是进程快

    IO密集型:任务都是纯io情况下,线程开销比进程小,肯定是线程好

    ```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())  # 本机为12核
        start=time.time()
        for i in range(12):
            # p=Process(target=work) #耗时8s多
            p=Thread(target=work) #耗时44s多
            l.append(p)
            p.start()
        for p in l:
            p.join()
        stop=time.time()
        print('run time is %s' %(stop-start))
    
    # 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()) #本机为12核
        start=time.time()
        for i in range(400):
            p=Process(target=work) #耗时12s多,大部分时间耗费在创建进程上
            # p=Thread(target=work) #耗时2s多
            l.append(p)
            p.start()
        for p in l:
            p.join()
        stop=time.time()
        print('run time is %s' %(stop-start))
    View Code

    2.GIL与自定义互斥锁

    不同的数据需要加不同的锁才能保证数据的安全,GIL锁只是对线程加锁,对数据并没有加锁的效果
    
    ```python
    from threading import Thread,Lock
    import time
    
    mutex=Lock()
    n=100
    def task():
        global n
        with mutex:
            temp=n
            time.sleep(0.1)
            n=temp-1
    
    if __name__ == '__main__':
        l=[]
        for i in range(100):
            t=Thread(target=task)
            l.append(t)
            t.start()
    
        for t in l:
            t.join()
        print(n)
    # 对于修改不同的数据,需要加不同的锁进行处理
    View Code

    3.死锁与递归锁(了解)

    自定义锁一次acquire必须对应一次release,不能连续acquire

    递归锁可以连续的acquire,每acquire一次计数加一

    from threading import Thread,Lock,RLock
    import time
    
    # mutexA=Lock()
    # mutexB=Lock()
    mutexB=mutexA=RLock()
    
    
    class Mythead(Thread):
        def run(self):
            self.f1()
            self.f2()
    
        def f1(self):
            mutexA.acquire()
            print('%s 抢到A锁' %self.name)
            mutexB.acquire()
            print('%s 抢到B锁' %self.name)
            mutexB.release()
            mutexA.release()
    
        def f2(self):
            mutexB.acquire()
            print('%s 抢到了B锁' %self.name)
            time.sleep(2)
            mutexA.acquire()
            print('%s 抢到了A锁' %self.name)
            mutexA.release()
            mutexB.release()
    
    if __name__ == '__main__':
        for i in range(100):
            t=Mythead()
            t.start()
    View Code

    4.信号量(了解)

    自定义的互斥锁如果是一个厕所,那么信号量就相当于公共厕所,门口挂着多个厕所的钥匙。抢和释放跟互斥锁一致

    from threading import Thread,Semaphore
    import time
    import random
    sm = Semaphore(5)  # 公共厕所里面有五个坑位,在厕所外面放了五把钥匙
    
    def task(name):
        sm.acquire()
        print('%s正在蹲坑'%name)
        # 模拟蹲坑耗时
        time.sleep(random.randint(1,5))
        sm.release()
    
    
    if __name__ == '__main__':
        for i in range(20):
            t = Thread(target=task,args=('伞兵%s号'%i,))
            t.start()
    View Code

    5.Event事件

    一些线程需要等待另外一些线程运行完毕才能运行,类似于发射信号一样

    from threading import Thread,Event
    import time
    event = Event()  # 造了一个红绿灯
    
    
    def light():
        print('红灯亮着的')
        time.sleep(3)
        print('绿灯亮了')
        event.set()
    
    
    def car(name):
        print('%s 车正在等红灯'%name)
        event.wait()
        print('%s 车加油门飙车走了'%name)
    
    
    if __name__ == '__main__':
        t = Thread(target=light)
        t.start()
    
        for i in range(10):
            t = Thread(target=car,args=('%s'%i,))
            t.start()
    View Code

    6.线程queue

    同一个进程下的线程数据都是共享的为什么还要用queue?queue本身自带锁的功能,能够保证数据的安全

    # 我们现在的q只能在本地使用,后面我们会学基于网络的q 
    import queue
    
    queue.Queue() #先进先出
    q=queue.Queue(3)
    q.put(1)
    q.put(2)
    q.put(3)
    print(q.get())
    print(q.get())
    print(q.get())
    
    queue.LifoQueue() #后进先出->堆栈
    q=queue.LifoQueue(3)
    q.put(1)
    q.put(2)
    q.put(3)
    print(q.get())
    print(q.get())
    print(q.get())
    
    queue.PriorityQueue() #优先级
    q=queue.PriorityQueue(3) #优先级,优先级用数字表示,数字越小优先级越高
    q.put((10,'a'))
    q.put((-1,'b'))
    q.put((100,'c'))
    print(q.get())
    print(q.get())
    print(q.get())
    View Code

     

  • 相关阅读:
    JavaScript 正则表达式
    git常用命令
    用纯css使内容永远居在页面底部
    Oracle中随机抽取N条记录
    表数据回复到某个时候
    oracle同名存储过程被覆盖后如何恢复(转)
    mybatis+spring+mysql
    定位
    关于js的闭包和复制对象
    idea展示runDashboard的窗口
  • 原文地址:https://www.cnblogs.com/guanchao/p/10832930.html
Copyright © 2011-2022 走看看