GIL(全局解析器锁)
GIL,全局解析器锁,只对多线程有影响。
如果没有GIL,多线程会同时调用全局资源,全局资源会因多个线程同时调用而造成数据错误。
因此每个线程在执行过程中都需要先获取GIL,保证同一时刻只有一个线程在执行代码,这样才不会造成数据错误。
多线程并不是真正“多线程”的例子
- 单进程下的CPU利用率
下面代码保存为single_thread.py
,同时运行两个single_thread.py
,两个程序各占满两个CPU核心,CPU利用率为100%。
# 主进程死循环,占用整个CPU
def single_thread():
while True:
pass
test()
- 多线程下的CPU利用率
下面代码保存为multithreading.py
,运行后发现,单进程内双线程即使同时死循环,两个核心中的CPU资源利用率都仅为50%。可以认为实际上只有一个核占满资源,和单线程没区别。
import threading
# 子进程死循环
def test_thread1():
while True:
pass
t1 = threading.Thread(target=test_thread1)
t1.start()
# 主进程死循环
def test_thread2():
while True:
pass
test_thread2()
- 多进程下的CPU利用率
下面代码保存为multiprocessing.py
,运行后发现CPU中两个核心的资源利用率为100%。
import multiprocessing
# 子进程
def test_multiprocessing1():
while True:
pass
m1 = multiprocessing.Process(target=test_multiprocessing1)
m1.start()
# 主进程
def test_multiprocessing2():
while True:
pass
test_multiprocessing2()
总结:多线程在死循环的程序中也不会导致CPU满资源的原因是GIL。全局解析器锁GIL的存在会限制多线程在运行中,实际仅有一个线程在执行任务。
- GIL不是python语言的问题,是python解析器的问题,cpython(C语言写的解析器)中存在这样的问题;
- 因此如果用的是cpython解析器,想要发挥多核性能,应该使用多进程;如果是其他解析器,多线程也能发挥多核性能;
- 爬虫程序中,多线程比单线程性能用提升,因为在遇到IO阻塞会自动释放GIL锁。
计算密集型程序和IO密集型程序
计算密集型
计算密集型程序,多用于计算和分析等领域的程序,GIL在这种程序中是个累赘,多线程等于单线程;多进程没GIL的影响,因此多进程适合计算密集型程序
IO密集型
IO,input和output输入输出类型,程序中常出现阻塞,计时等需要耗时的情况,GIL能在线程阻塞时自动释放GIL锁,其他线程就能执行代码,形成多线程并发的假象;
总结:多线程适用于IO密集型程序
用C语言解决GIL锁问题
多线程任务用C语言编写,通过在python代码中导入C语言文件解决多线程中GIL的问题
- 下面代码保存为
deadloop.c
文件
void DeadLoop(){
while(1){
;
}
}
- 通过
gcc 文件名.c -shared -o lib文件名.so
编译后得到libdeadloop.so
动态库文件 - 将下面代码保存为
solution.py
文件,文件中加载了libdeadloop.so
动态库中的DeadLoop()
函数作为子线程
from ctypes import *
import threading
# 加载动态库libdeadloop
lib = cdll.LoadLibrary("./libdeadloop.so")
# 创建一个子线程,加载动态库中的DeadLoop()函数,此函数是一个死循环
t = threading.Thread(target=lib.DeadLoop)
t.start()
# 主线程
def multi_threading2():
while True:
pass
multi_threading2()
总结:此时运行
solution.py
文件,多线程中为两个死循环,占用的CPU资源比为100%,证明解决了GIL锁线程的问题