为什么GIL不允许表现不佳的线程代码?

时间:2017-07-06 16:07:04

标签: python multithreading python-3.x gil

顺序代码(seq.py),

所用的时间
import time

def countDown(n):
    while n > 0:
        n -= 1

n = 50000000
start = time.time()
countDown(n)
end = time.time()
print(end-start)

是,

$ python3.6 seq.py 
 4.209718227386475
$ python3.6 seq.py 
 4.007786750793457
$ python3.6 seq.py 
 4.0265843868255615
$

线程版本(usingThreads.py),

所用的时间
from threading import Thread
import time
def countDown(n):
    while n > 0:
        n -= 1


n = 50000000

t1 = Thread(target=countDown, args=(n//2,))
t1.daemon = True
t2 = Thread(target=countDown, args=(n//2,))
t2.daemon = True

start = time.time()
t1.start()
t2.start()
t1.join()
t2.join()
end = time.time()
print(end-start)

是,

$ python3.6 usingThreads.py 
 4.1083903312683105
$ python3.6 usingThreads.py 
 4.093154668807983
$ python3.6 usingThreads.py 
 4.092989921569824
$ python3.6 usingThreads.py 
 4.116031885147095
$
$ nproc
 4
$

Python解释器不应允许CPU绑定线程释放GIL。

期望usingThreads.py的执行时间超过seq.py,因为,

1)任何一个线程一次都在执行,尽管有4个核心

2)尝试通过thread2从thread1获取GIL失败所花费的时间(反之亦然)应该会延迟执行。

修改

使用n=500000000

$ python3.6 seq.py 
 40.22602105140686
$ python3.6 seq.py 
 40.510098457336426
$ python3.6 seq.py 
 40.04688620567322
$
$ python3.6 usingThreads.py 
 40.91394829750061
$ python3.6 usingThreads.py 
 42.30081081390381
$ python3.6 usingThreads.py 
 41.328694581985474

问题:

为什么usingThread.py的效果优于seq.py

1 个答案:

答案 0 :(得分:0)

这两个版本的代码执行的工作量相同,因此花费的时间几乎相同(均为50000000次)。

gil使得它们不会并行运行(因此线程版本不会更快)但是上下文切换的开销相对较小,所以你得到了几乎相同的结果。

这里有一个解释http://www.dabeaz.com/python/UnderstandingGIL.pdf

他正在使用与你相同的例子,在这个演示中,当他使用一台cpus超过1的计算机时,他得到了一个较慢的线程版本,他解释得很好,当你使用超过1个cpu时,你会得到更多的开销(更多尝试上下文切换)会使你的程序变慢