2012-06-28 280 views
2

我第一次在生产代码观察这个问题,然后做了一个原型:Python的多线程代码是慢于单线程

import threading, Queue, time, sys 

def heavyfunc(): 
    ''' The idea is just to load CPU ''' 
    sm = 0 
    for i in range(5000): 
     for j in range(5000): 
      if i + j % 2 == 0: 
       sm += i - j 
    print "sm = %d" % sm 

def worker(queue): 
    ''' worker thread ''' 
    while True: 
     elem = queue.get() 
     if elem == None: break 
     heavyfunc()   # whatever the elem is 


starttime = time.time() 

q = Queue.Queue()    # queue with tasks 

number_of_threads = 1 
# create & start number_of_threads working threads 
threads = [threading.Thread(target=worker, args=[q]) for thread_idx in range(number_of_threads)] 
for t in threads: t.start() 

# add 2 working items: they are estimated to be computed in parallel 
for x in range(2): 
    q.put(1) 

for t in threads: q.put(None) # Add 2 'None' => each worker will exit when gets them 
for t in threads: t.join() # Wait for every worker 

#heavyfunc() 

elapsed = time.time() - starttime 

print >> sys.stderr, elapsed 

heavyfunc的想法()仅仅是加载CPU,没有任何同步和依赖。

当使用1个线程,需要4.14秒中平均 当使用2个线程,它在不使用任何线程,以计算heavyfunc()采用6.40秒中平均 取2.07秒的平均(测定多次,这是正好是4.14/2,就像1个线程和2个任务一样)。

我在等2个线程期望2个带有heavyfunc()的作业需要2.07秒。 (我的CPU是i7 =>有足够的核心)。

这里是CPU显示器的屏幕截图也给有没有真正的多线程的想法:

CPU Load Graphi

哪里是在我的思想的错误?如何创建不干扰的n个线程?

+1

正如在线程模块的文档开始的相当大的笔记所说.. CPython中只有一个线程,使用进程。 – Voo

+0

这是众所周知的Python行为,请参阅[了解Python GIL](http://www.dabeaz.com/GIL/)。 –

+0

另请参阅多进程模块。 –

回答

4

CPython不会在一个以上的内核上执行字节码。多线程cpu绑定代码毫无意义。全局解释器锁(GIL)用于保护进程中的所有引用计数,因此一次只能有一个线程使用Python对象。

您看到的性能更差,因为您一次只工作一个线程,但现在您还在更改线程上下文。

+0

在这种情况下,从线程切换到多处理可能会很有用。就像我看到的那样,Python似乎很好地利用线程来处理不是CPU密集型的任务,但是这可能会由于其他原因(通常是I/O,比如对Web请求的请求)而放慢代码。如果我们有CPU密集型任务将您的机器推到极限,多处理可能会打开另一扇门,让您可以同时完成更多工作。 –