3

几个小时前,我问了一个关于 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 循环慢?

4

1 回答 1

2

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

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

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

为什么?

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

但是,为什么内置的 sum 比 Python 无线程解决方案要快呢?内置的 sum 是用 C 实现的,而 Python 循环在性能方面很糟糕。因此使用内置总和迭代列表的所有元素要快得多。

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

于 2012-09-19T13:36:10.263 回答