在python中,实际上多线程都是假的,因为有个GIL(全局解释锁),无论你有多少个线程,在最终执行的时候都只有一个CPU被调用
如果要真正意义上实现多线程的话可以使用C语言作为扩展
在解释GIL问题,我先演示一下单核CPU和多核CPU
这里是我单核CPU的处理情况
然后我运行一个死循环
while True:
pass
CPU一下子就满了
接下来试试两核的CPU
这里我创建一个子线程也是死循环,主线程也是死循环,那么就有两个死循环了
代码如下:
import threading
def test():
while True:
pass
t1=threading.Thread(target=test)
t1.start()
while True:
pass
执行前我的资源如下:
执行后:
哦吼,两个CPU却不是100%,而是两个各占50%
我们从理论上来说,我们两个线程应该各占一个CPU
如图:
但实际上调用的时候,Python有一个GIL,也就是全局解释锁
GIL会控制两个线程,线程1来就给一个CPU去执行,线程1执行完再给线程2给一个CPU执行,实际上是单线程的方式执行的
那么一定要解决这个问题怎么办呢,这个可以用C语言去扩展来解决这个问题
C语言的代码如下
void DeadLoop()
{
while(1)
{
;
}
}
然后在当前目录输入如下命令
gcc loop.c -shared -o libdeadloop.so
然后编写Python代码
from threading import Thread
from ctypes import *
lib = cdll.LoadLibrary("./libdeadloop.so")
t = Thread(target=lib.DeadLoop)
t.start()
#lib.DeadLoop()
while True:
pass
然后运行python代码
一下子就满了
GIL锁是当初设计python的时候为了数据安全所做的决定,程序执行的时候都要经过GIL,而GIL分配一个程序给一个CPU,所以每个CPU都只能执行一个线程,所以GIL的原因导致python无法使用多CPU的特性,但是GIL是会释放的
在python2.x的中,GIL会按字节码的执行的数量来释放。
在python3.x中会按照时间片来释放,即隔一段时间就释放一次,遇到IO也会释放、或者遇到其他的一些需要时间很多的部分也会释放
IO操作是整个系统中最占时间的部分,比一个单单的线程要高出上千上万倍,如果遇到IO还阻塞的话那真的是会让程序变得很慢了。即便是有很多线程,每个线程切换的时间都要远远低于IO操作,这部分切换的时间在运行程序中是微不足道的
所以,这样在IO型操作就相比于java等语言也不会慢到哪里去
计算型任务基本上是完全占CPU,这个时候要么就用C语言重写关键部分,要么就直接调用多进程,一个进程就一个GIL,这样也可以完美的解决这个问题
计算型操作这里我拿个案例来说明
from threading import Thread
total=0
def add():
global total
for i in range(1000000):
total+=1
def desc():
global total
for i in range(1000000):
total-=1
if __name__ == "__main__":
addtest=Thread(target=add)
desctest=Thread(target=desc)
addtest.start()
desctest.start()
addtest.join()
desctest.join()
print(total)
#611367
#-433619
执行结果都不一样
如果python没有时间片释放机制,那么肯定是执行完add再去执行desc,结果肯定是0。就是因为有时间片释放机制,所以每次的时间都不一样