并发编程~~~多线程~~~守护线程, 互斥锁, 死锁现象与递归锁, 信号量 (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锁上锁,释放无需手动操作.

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

原文地址:https://www.cnblogs.com/lav3nder/p/11802298.html