什么是GIL?
- GIL:全局解释器锁。每个线程在执行的过程都需要先获取GIL,保证同一时刻只有一个线程可以执行代码。
- GIL锁是Cpython解释器中特有的,在JPython、PyPy中没有GIL锁.
为什么使用gil锁
- 线程的特点是数据资源是共享的,如果多个线程都要共享Cpython的解释权限,共享意味着竞争,有竞争数据就不安全,所以Cpython的GIL锁就产生了.
- python是在1991诞生的,它是用C语言实现的,并能够调用C语言的库文件;那时候的电脑还没有多核,使用gil锁可以实现多线程,但是随着电脑技术的发展,多核CPU的出现,导致gil锁,不能充分发挥多核的优势,只能在单核中运行,所以cpu的多核优势也没有了,除非多开Cpython解释器或多进程,否则同时只能运行一个线程.
- 曾经也有大神尝试去修改这一历史遗留问题,但是后来发现任务量太大,十几年的发展,Cpython解释器代码和与它衍生的功能代码数量可想可知,
python执行过程
- ① 把文本形式的源代码解析并编译成字节码(机器可识别的二进制码)。
- ② 用一种基于栈的解释器来运行这份字节码。
- 每次执行Python程序,都会产生一个独立的进程。在一个python进程中,不仅有运行程序主线程或由该主线程开启的其他线程,还有解释器开启的垃圾回收等解释器级别的线程。
- 这其中,程序代码作为一种数据也是被所有线程所共享的包括解释器的所有代码。所有线程的任务,都需要将任务的代码当作参数传给解释器的代码去执行,各线程想要完成自己的任务首先需要解决的时访问到解释器。
如何避免受到GIL的影响
- 1.更换解释器
- 2.使用多进程
- 3.协程
- 4.多线程部分的代码使用其他语言
互斥锁
什么是互斥锁
- 简单讲就是对共享数据进行锁定,保证同一时刻只能有一个线程去操作。它是用户级别的锁,
- 抢到锁的线程先执行,没有抢到锁的线程需要等待,等锁用完后需要释放,然后其它等待的线程再去抢这个锁,那个线程抢到那个线程再执行。
具体那个线程抢到这个锁我们决定不了,是由cpu调度决定的。
- 一个进程中多线程共享全局变量,如果没有互斥锁,会造成资源竞争,导致数据发生错误
num = 0
def work1():
global num
print(threading.enumerate())
for i in range(1000000):
mutex.acquire()
num += 1
mutex.release()
def work2():
global num
print(threading.enumerate())
for i in range(1000000):
mutex.acquire()
num += 1
mutex.release()
def main():
"""创建线程"""
print(threading.enumerate())
thread_work1 = threading.Thread(target=work1).start()
thread_work2 = threading.Thread(target=work2).start()
- 多线程执行变成了包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了