Python线程优于简单的while循环或线程优化

时间:2012-05-29 16:56:34

标签: python multithreading thread-safety

几个小时前,我问了一个关于Python多线程的问题。为了理解它是如何工作的,我做了一些实验,这是我的测试:


使用线程的Python脚本:

import threading
import Queue
import time

s = 0;

class ThreadClass(threading.Thread):

    lck = threading.Lock()

    def __init__(self, inQ, outQ):
        threading.Thread.__init__(self)
        self.inQ = inQ
        self.outQ = outQ

    def run(self):
        while True:
            global s
            #print self.getName()+" is running..."
            self.item = self.inQ.get()
            #self.inQ.task_done()
            ThreadClass.lck.acquire()
            s += self.item
            ThreadClass.lck.release()
            #self.inQ.task_done()
            self.outQ.put(self.item)
            self.inQ.task_done()

inQ = Queue.Queue()
outQ = Queue.Queue()

i = 0
n = 1000000

print "putting items to input"
while i<n:
    inQ.put(i)
    i += 1

start_time = time.time()
print "starting threads..."
for i in xrange(10):
    t = ThreadClass(inQ, outQ);
    t.setDaemon(True)
    t.start()


inQ.join()
end_time = time.time()
print "Elapsed time is: %s"%(end_time - start_time)
print s

以下内容与简单的while循环具有相同的功能:

import Queue
import time

inQ = Queue.Queue()
outQ = Queue.Queue()

i = 0
n = 1000000
sum = 0

print "putting items to input"
while i<n:
    inQ.put(i)
    i += 1

print "while loop starts..."
start_time = time.time()
while inQ.qsize() > 0:
    item = inQ.get()
    sum += item
    outQ.put(item)
end_time = time.time()

print "Elapsed time is: %s"%(end_time - start_time)
print sum

如果在您的计算机上运行这些程序,您可以看到线程比简单的while循环慢得多。我对线程有点困惑,想知道线程代码有什么问题。如何优化它(在这种情况下),以及为什么它比while循环慢?

1 个答案:

答案 0 :(得分:2)

线程总是很棘手,因为Python中的线程是特殊的。

要讨论优化,你必须专注于特殊情况,否则没有单一的答案。 我的计算机上的初始线程解决方案在37.11秒运行。如果使用局部变量对每个线程的元素求和,然后仅在最后锁定,则时间下降到32.62秒。

确定。无线程解决方案在7.47秒运行。大。但是如果你想在Python中总结大量数字,你只需使用内置函数和。因此,如果我们使用没有线程的List和内置的总和,则时间下降到0.09秒。太好了!

为什么?

Python中的线程受全局解释器锁(GIL)的约束。他们永远不会并行运行Python代码。它们是真正的线程,但在内部,它们只允许在将GIL发布到另一个线程之前运行X Python指令。对于非常简单的计算,创建线程,锁定和上下文切换的成本远远大于简单计算的成本。所以在这种情况下,开销是计算本身的5倍。当你不能使用异步I / O或者你有同时运行的阻塞函数时,Python中的线程很有意思。

但是,为什么内置的总和比Python无线程解决方案更快?内置的总和是用C实现的,Python循环很好地吸收了性能。因此,使用内置总和迭代列表的所有元素要快得多。

总是这样吗?不,这取决于你在做什么。如果您将这些数字写入n个不同的文件,则线程解决方案可能有机会,因为在I / O期间释放GIL。但即使这样,我们也需要检查I / O缓冲/磁盘同步时间是否不会改变游戏规则。这种细节使得最终答案非常困难。因此,如果您想要优化某些内容,您必须拥有优化所需的内容。要汇总Python中的数字列表,只需使用内置的总和。