• 并发编程~~~多线程~~~守护线程, 互斥锁, 死锁现象与递归锁, 信号量 (Semaphore), GIL全局解释器锁


    一 守护线程

    from threading import Thread
    import time
    def foo():
        print(123)
        time.sleep(1)
        print('end123')
    
    def bar():
        print(456)
        time.sleep(3)
        print('end456')
    
    t1 = Thread(target=foo)
    t2 = Thread(target=bar)	
    
    t1.daemon = True
    t1.start()
    t2.start()
    print('主线程')
    # 123
    # 456
    # 主线程
    # end123
    # end456
    

    守护线程 等待非守护子线程以及主线程结束之后,结束.

    二 互斥锁

    from threading import Thread,Lock
    x = 100
    def task(lock):
        lock.acquire()
        global x
        x -= 1
        lock.release()
    
    if __name__ == '__main__':
        lock = Lock()
        lst = []
        for i in range(100):
            t = Thread(target=task,args=(lock,))
            lst.append(t)
            t.start()
        for i in lst:
            i.join()
        print(f'主线程{x}')
    

    三 死锁现象与递归锁

    1.死锁:

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

    2.递归锁:

    解决死锁的方法
    
    from threading import Thread,RLock
    lock_A = lock_B = RLock()
    
    class MyThread(Thread):
    
        def run(self):
            self.f1()
            self.f2()
    
        def f1(self):
            lock_A.acquire()
            print(f'{self.name}拿到了A锁')
            lock_B.acquire()
            print(f'{self.name}拿到了B锁')
            lock_B.release()
            lock_A.release()
    
        def f2(self):
            lock_B.acquire()
            print(f'{self.name}拿到了B锁')
            lock_A.acquire()
            print(f'{self.name}拿到了A锁')
            lock_A.release()
            lock_B.release()
    
    if __name__ == '__main__':
        for i in range(3):
            t = MyThread()
            t.start()
    

    四 信号量 (Semaphore)

    信号量也是一种锁,控制并发数量

    from threading import Thread,Semaphore,current_thread
    import time,random
    sem = Semaphore(5)
    def task():
        sem.acquire()
        print(f'{current_thread().name}厕所ing')
        time.sleep(random.randint(1,3))
        sem.release()
    
    if __name__ == '__main__':
        for i in range(20):
            t = Thread(target=task,)
            t.start()
    

    五 GIL全局解释器锁

    理论上来说: 单个进程的多线程可以利用多核

    但是,开发开发CPython解释器的程序员给进入解释器的线程加了锁.同一时刻只能允许一个线程进入解释器

    为什么加锁?

    1. 当时都是单核时代,而且CPU价格非常贵

    2. 如果不加全局解释器锁,开发CPython解释器的程序员就会在源码内部各种主动加锁,非常麻烦,各种死锁现象等等.为了省事,进入解释器时直接给线程加一个锁

      优点: 保证了CPython解释器的数据资源的安全.

      缺点: 单个进程的多线程不能利用多核

      Jython和pypy没有GIL锁

    单个进程的多线程可以并发,但不能利用多核,不能并行

    遇到IO阻塞,CPU就会被操作系统切走,GIL锁被释放,线程挂起,另一个线程进入,可以实现并发

    多个进程可以并发,并行

    IO密集型: 单个进程的多线程适合并发执行.

    计算密集型: 多进程的并行

    六 GIL与lock锁的区别

    相同点: 都是同种锁,互斥锁

    不同点:

    ​ GIL锁保护解释器内部的资源数据的安全. GIL锁上锁,释放无需手动操作.

    ​ 自定义的互斥锁保护进程中的资源数据的安全, 必须自己手动上锁,释放锁

  • 相关阅读:
    MFC CDialog/CDialogEx DoModal ALT
    yum和apt-get用法及区别
    ubuntu 12.04 source.list 源更新
    elasticsearch 优化
    TRIE树
    数据统计经验浅谈
    机器学习
    python 读取libsvm文件
    spark 参数调优
    python中的一些函数
  • 原文地址:https://www.cnblogs.com/lav3nder/p/11802298.html
Copyright © 2020-2023  润新知