python GIL的多线程示例

时间:2017-07-18 04:48:05

标签: python multithreading python-multithreading gil

在编写多线程代码时,我已经阅读了很多关于这个python GIL业务的“糟糕”的内容,但我从未见过一个例子。有人可以给我一个基本的例子,说明当使用线程时GIL何时会出现问题。

谢谢!

1 个答案:

答案 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的语言。