目录
线程相关锁 一、线程同步锁(互斥锁) 多线程抢占资源的数据安全问题使用线程同步锁解决数据安全问题二、死锁三、递归锁四、全局解释器锁GIL 所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程,如下就是死锁:
# 著名的科学家吃面问题 from threading import Thread,Lock import time noodle_lock = Lock() fork_lock = Lock() def eat1(name): noodle_lock.acquire() print(f'{name}拿到面条了') fork_lock.acquire() time.sleep(1) print(f'{name}拿到叉子了') print(f'{name}吃面') fork_lock.release() noodle_lock.release() def eat2(name): fork_lock.acquire() print(f'{name}拿到叉子了') time.sleep(1) noodle_lock.acquire() print(f'{name}拿到面条了') print(f'{name}吃面') noodle_lock.release() fork_lock.release() Thread(target=eat1,args=('alex',)).start() Thread(target=eat2,args=('nick',)).start() Thread(target=eat1,args=('tank',)).start() Thread(target=eat2,args=('egon',)).start() ''' alex拿到面条了 alex拿到叉子了 alex吃面 nick拿到叉子了 tank拿到面条了 ''' 死锁的解决方法就是递归锁,在Python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。
这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。上面的例子如果使用RLock代替Lock,则不会发生死锁:
from threading import Thread,RLock import time fork_lock = noodle_lock = RLock() def eat1(name): noodle_lock.acquire() print(f'{name}拿到面条了') fork_lock.acquire() print(f'{name}拿到叉子了') print(f'{name}吃面') fork_lock.release() noodle_lock.release() def eat2(name): fork_lock.acquire() print(f'{name}拿到叉子了') time.sleep(1) noodle_lock.acquire() print(f'{name}拿到面条了') print(f'{name}吃面') noodle_lock.release() fork_lock.release() Thread(target=eat1,args=('alex',)).start() Thread(target=eat2,args=('nick',)).start() Thread(target=eat1,args=('tank',)).start() Thread(target=eat2,args=('egon',)).start() ''' alex拿到面条了 alex拿到叉子了 alex吃面 nick拿到叉子了 nick拿到面条了 nick吃面 tank拿到面条了 tank拿到叉子了 tank吃面 egon拿到叉子了 egon拿到面条了 egon吃面 ''' Python代码的执行由Python虚拟机(也叫解释器主循环)来控制。Python在设计之初就考虑到要在主循环中,同时只有一个线程在执行。虽然 Python 解释器中可以“运行”多个线程,但在任意时刻只有一个线程在解释器中运行。 对Python虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同一时刻只有一个线程在运行。
在多线程环境中,Python 虚拟机按以下方式执行:
a、设置 GIL;
b、切换到一个线程去运行;
c、运行指定数量的字节码指令或者线程主动让出控制(可以调用 time.sleep(0));
d、把线程设置为睡眠状态;
e、解锁 GIL;
d、再次重复以上所有步骤。 在调用外部代码(如 C/C++扩展函数)的时候,GIL将会被锁定,直到这个函数结束为止(由于在这期间没有Python的字节码被运行,所以不会做线程切换)编写扩展的程序员可以主动解锁GIL。
GIL 就是当线程访问cpu对数据进行修改时介于两者之间的一把锁,其实就是Cpython的解释器起作用。 他控制了同一时刻只能有一个线程来访问数据,将此线程设置为睡眠状态 当线程对数据修改完毕了,GIL就会释放该线程 之后才允许下一个线程来访问数据 GIL锁的是线程 GIL导致的不能并发问题是Cpython的特性,目前还不能得以解决!转载于:https://www.cnblogs.com/dadazunzhe/p/11545522.html