zoukankan      html  css  js  c++  java
  • GIL全局解释器锁、死锁、递归锁以及event事件与信号量、线程queue

    一.GIL全局解释器锁

    """
    In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple
    native threads from executing Python bytecodes at once. This lock is necessary mainly
    because CPython’s memory management is not thread-safe. (However, since the GIL
    exists, other features have grown to depend on the guarantees that it enforces.)
    """

      GIL是一个互斥锁:保护数据的安全(以牺牲运行效率来换取数据的安全)

      阻止同一进程内多个线程同时执行(不能并行但是能够实现并发)

      GIL全局解释器锁存在的原因是因为Cpython解释器的内存管理(垃圾处理机制)不是线程安全的

      结论:在Cpython解释器中,同一个进程下开启的多线程,同一时刻只能有一个线程执行,无法利用多核优势

      1.那么python中多线程是否就没有作用了?

       分情况考虑:

        1.存在四个计算密集型的任务,每个任务耗时10S

        单核情况下:

          多线程好一点,消耗的资源少一点

        多核情况下:

          开四个进程:运行时间10S多一点

          开四个线程:运行时间40S多一点

        2.存在四个IO密集型的任务,每个任务IO 10S

        单核情况下:

          多线程好一点,多进程开启切换的时间较长

        多核情况下:

          多线程好一点,多进程开启切换的时间较长

      总结:多线程和多进程都有自己的优点,要根据项目需求合理选择

      测试结果:

     1 # 计算密集型
     2 from multiprocessing import Process
     3 from threading import Thread
     4 import os,time
     5 def work():
     6     res=0
     7     for i in range(100000000):
     8         res*=i
     9 
    10 
    11 if __name__ == '__main__':
    12     l=[]
    13     print(os.cpu_count())  # 本机为12核
    14     start=time.time()
    15     for i in range(12):
    16         # p=Process(target=work) #耗时8s多
    17         p=Thread(target=work) #耗时44s多
    18         l.append(p)
    19         p.start()
    20     for p in l:
    21         p.join()
    22     stop=time.time()
    23     print('run time is %s' %(stop-start))
    24 
    25 # IO密集型
    26 from multiprocessing import Process
    27 from threading import Thread
    28 import threading
    29 import os,time
    30 def work():
    31     time.sleep(2)
    32 
    33 
    34 if __name__ == '__main__':
    35     l=[]
    36     print(os.cpu_count()) #本机为12核
    37     start=time.time()
    38     for i in range(400):
    39         p=Process(target=work) #耗时12s多,大部分时间耗费在创建进程上
    40         # p=Thread(target=work) #耗时2s多
    41         l.append(p)
    42         p.start()
    43     for p in l:
    44         p.join()
    45     stop=time.time()
    46     print('run time is %s' %(stop-start))
    测试结果

    二.GIL与普通锁对比

    from threading import Thread,Lock
    import time
    
    mutex = Lock()
    
    n = 100
    
    def task():
        global n
        mutex.acquire()
        tmp = n
        time.sleep(0.1)
        n = tmp - 1
        mutex.release()
    
    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)

      总结:对于不同的数据,想要保证安全,需要加不同的锁处理

         GIL并不能保证数据的安全,它是对Cpython解释器加锁,针对的是线程安全

         保证的是在同一进程下多个线程之间的安全

    三.死锁与递归锁

      死锁现象:

    from threading import Thread, Lock
    import time
    
    
    mutexA = Lock()
    mutexB = Lock()
    
    class MyThread(Thread):
        def run(self):
            self.func1()
            self.func2()
    
        def func1(self):
            mutexA.acquire()
            print('%s 抢到了 A锁' % self.name)
            mutexB.acquire()
            print('%s 抢到了 B锁' % self.name)
            mutexA.release()
            print('%s 释放了 A锁' % self.name)
            mutexB.release()
            print('%s 释放了 B锁' % self.name)
    
        def func2(self):
            mutexB.acquire()
            print('%s 抢到了 B锁' % self.name)
            time.sleep(0.1)
            mutexA.acquire()
            print('%s 抢到了 A锁' % self.name)
            mutexB.release()
            print('%s 释放了 B锁' % self.name)
            mutexA.release()
            print('%s 释放了 A锁' % self.name)
    
    
    for i in range(10):
        t = MyThread()
        t.start()

      递归锁:

    from threading import Thread, RLock
    import time
    
    
    # mutexA = Lock()
    # mutexB = Lock()
    start = time.time()
    mutexB = mutexA = RLock()
    
    
    class MyThread(Thread):
        def run(self):
            self.func1()
            self.func2()
    
        def func1(self):
            mutexA.acquire()
            print('%s 抢到了 A锁' % self.name)
            mutexB.acquire()
            print('%s 抢到了 B锁' % self.name)
            mutexA.release()
            print('%s 释放了 A锁' % self.name)
            mutexB.release()
            print('%s 释放了 B锁' % self.name)
    
        def func2(self):
            mutexB.acquire()
            print('%s 抢到了 B锁' % self.name)
            time.sleep(0.1)
            mutexA.acquire()
            print('%s 抢到了 A锁' % self.name)
            mutexB.release()
            print('%s 释放了 B锁' % self.name)
            mutexA.release()
            print('%s 释放了 A锁' % self.name)
    
    
    for i in range(10):
        t = MyThread()
        t.start()

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

        递归锁可以连续的acquire,每acquire一次计数+1,只针对第一次抢到的人

    四.event事件

    from threading import Thread, Event
    import time
    
    
    event = Event()
    
    
    def light():
        print('红灯亮')
        time.sleep(5)
        event.set() # 给event发信号,解除阻塞
        print('绿灯亮')
    
    
    def car(i):
        print('车%s is stop' % i)
        event.wait() # 阻塞
        print('车%s is run' % i)
    
    
    l = Thread(target=light)
    l.start()
    
    
    for i in range(10):
        c = Thread(target=car,args=(i,))
        c.start()

    五.信号量

    from threading import Thread, Semaphore
    import time
    import random
    
    sm = Semaphore(5) # 五个厕所五把锁
    
    # 跟你普通的互斥锁区别在于,普通的互斥锁是独立卫生间,所有人抢一把锁
    # 信号量 公共卫生间 有多个坑,所有人抢多把锁

    def wait(name): with sm: print('%s is WCing' % name) time.sleep(random.randint(1,5)) for i in range(10): t = Thread(target=wait,args=(i,)) t.start()  

    五.信号量

    import queue
    
    # 1.普通q
    # 2.先进后出q
    # 3.优先级q
    
    # 1.普通q
    q = queue.Queue(3)
    q.put(1)
    q.put(2)
    q.put(3)
    print(q.get())
    print(q.get())
    print(q.get())
    
    # 先进后出q
    q = queue.LifoQueue(5)
    q.put(1)
    q.put(2)
    q.put(3)
    q.put(4)
    print(q.get())
    
    # 优先级q
    q = queue.PriorityQueue()
    q.put((10, 'a'))
    q.put((-1, 'b'))
    q.put((100, 'c'))
    print(q.get())
    print(q.get())
    print(q.get())

      

     

  • 相关阅读:
    OSPF的简易模拟配置第二篇
    OSPF的简易模拟配置第一篇
    RIP简易配置第二篇
    linux常用命令集(压缩和归档操作-共16个)
    简易路由重分布配置
    linux常用命令集(磁盘和文件系统操作-共15个)
    简易的浮动静态路由配置
    神州数码DHCP及DHCP中继配置
    神州数码HSRP(热备份路由协议)配置
    神州数码策略路由(PBR)配置
  • 原文地址:https://www.cnblogs.com/majingjie/p/10832686.html
Copyright © 2011-2022 走看看