在编写多线程代码时,我已经阅读了很多关于这个python GIL业务的“糟糕”的内容,但我从未见过一个例子。有人可以给我一个基本的例子,说明当使用线程时GIL何时会出现问题。
谢谢!
答案 0 :(得分:12)
多线程的一个主要原因是程序可以利用多个CPU(和/或CPU上的多个核心)来计算每秒更多的操作。但是在Python中,GIL意味着即使你有多个线程在计算上同时进行,但在任何给定的瞬间,其中只有一个线程实际上会运行,因为所有其他线程都将被阻塞,等待获取全局解释器锁。这意味着多线程Python程序实际上比单线程版本更慢,而不是更快,因为一次只运行一个线程 - 加上强制每个线程产生的会计开销每隔几毫秒等待,获取,然后放弃GIL(循环式)。
为了证明这一点,这里有一个玩具Python脚本,它产生指定数量的线程,然后作为它的"计算"每个线程只是不断递增一个计数器,直到5秒钟过去。最后,主线程计算出发生的反增量总数并打印总数,以便我们测量多少"工作"在5秒钟内完成。
import threading
import sys
import time
numSecondsToRun = 5
class CounterThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self._counter = 0
self._endTime = time.time() + numSecondsToRun
def run(self):
# Simulate a computation on the CPU
while(time.time() < self._endTime):
self._counter += 1
if __name__ == "__main__":
if len(sys.argv) < 2:
print "Usage: python counter 5"
sys.exit(5)
numThreads = int(sys.argv[1])
print "Spawning %i counting threads for %i seconds..." % (numThreads, numSecondsToRun)
threads = []
for i in range(0,numThreads):
t = CounterThread()
t.start()
threads.append(t)
totalCounted = 0
for t in threads:
t.join()
totalCounted += t._counter
print "Total amount counted was %i" % totalCounted
....这是我在计算机上得到的结果(这是一款启用了超线程的双核Mac Mini,FWIW):
$ python counter.py 1
Spawning 1 counting threads for 5 seconds...
Total amount counted was 14210740
$ python counter.py 2
Spawning 2 counting threads for 5 seconds...
Total amount counted was 10398956
$ python counter.py 3
Spawning 3 counting threads for 5 seconds...
Total amount counted was 10588091
$ python counter.py 4
Spawning 4 counting threads for 5 seconds...
Total amount counted was 11091197
$ python counter.py 5
Spawning 5 counting threads for 5 seconds...
Total amount counted was 11130036
$ python counter.py 6
Spawning 6 counting threads for 5 seconds...
Total amount counted was 10771654
$ python counter.py 7
Spawning 7 counting threads for 5 seconds...
Total amount counted was 10464226
注意第一次迭代(仅产生一个工作线程)实现了最佳性能;当多个线程同时运行时,计数生产率大幅下降。这显示了GIL如何削弱Python中的多线程性能 - 用C语言编写的相同程序(或没有GIL的任何其他语言)在运行更多线程时会表现出更好的性能,而不是更差(直到工作线程的数量与当然,硬件上的核心数量。)
这并不意味着多线程在Python中完全没用 - 但在大多数或所有线程被阻塞等待I / O而不是CPU限制的情况下,它仍然有用。这是因为等待I / O被阻塞的Python线程在等待时不会锁定GIL,因此在此期间其他线程仍然可以自由执行。但是,如果您需要并行化计算密集型任务(例如光线跟踪或计算Pi或代码破解或类似的所有数字),那么您将要使用多个进程而不是多个线程,或者使用不同的没有GIL的语言。