我正在尝试在python中找出多线程编程。这是我想要比较串行和并行速度的简单任务。
import threading
import Queue
import time
import math
def sinFunc(offset, n):
result = []
for i in range(n):
result.append(math.sin(offset + i * i))
return result
def timeSerial(k, n):
t1 = time.time()
answers = []
for i in range(k):
answers.append(sinFunc(i, n))
t2 = time.time()
print "Serial time elapsed: %f" % (t2-t1)
class Worker(threading.Thread):
def __init__(self, queue, name):
self.__queue = queue
threading.Thread.__init__(self)
self.name = name
def process(self, item):
offset, n = item
self.__queue.put(sinFunc(offset, n))
self.__queue.task_done()
self.__queue.task_done()
def run(self):
while 1:
item = self.__queue.get()
if item is None:
self.__queue.task_done()
break
self.process(item)
def timeParallel(k, n, numThreads):
t1 = time.time()
queue = Queue.Queue(0)
for i in range(k):
queue.put((i, n))
for i in range(numThreads):
queue.put(None)
for i in range(numThreads):
Worker(queue, i).start()
queue.join()
t2 = time.time()
print "Serial time elapsed: %f" % (t2-t1)
if __name__ == '__main__':
n = 100000
k = 100
numThreads = 10
timeSerial(k, n)
timeParallel(k, n, numThreads)
#Serial time elapsed: 2.350883
#Serial time elapsed: 2.843030
有人可以向我解释发生了什么事吗?我已经习惯了C ++,使用该模块的类似版本看到了我们期望的加速。
答案 0 :(得分:14)
其他答案提到了GIL是cpython中的问题。但我觉得有一些遗漏的信息。在线程中运行的代码受CPU限制的情况下,这将导致性能问题。在你的情况下,是的,在线程中做很多计算很可能会导致性能急剧下降。
但是,如果您正在执行更多IO绑定的操作,例如从网络应用程序中的许多套接字读取,或者调用子进程,则可以从线程中获得性能提升。上面代码的一个简单示例是向shell添加一个简单的简单调用:
import os
def sinFunc(offset, n):
result = []
for i in xrange(n):
result.append(math.sin(offset + i * i))
os.system("echo 'could be a database query' >> /dev/null; sleep .1")
return result
那个调用可能就像等待文件系统一样真实。但是你可以看到,在这个例子中,线程将开始证明是有益的,因为当线程在IO上等待并且其他线程将继续处理时可以释放GIL。即便如此,当更多线程开始被创建它们并同步它们的开销所抵消时,仍然有一个最佳点。
对于CPU绑定代码,您可以使用multiprocessing
来自文章:http://www.informit.com/articles/article.aspx?p=1850445&seqNum=9
...线程更适合I / O绑定的应用程序(I / O版本 GIL,允许更多的并发)......
关于线程与进程的类似问题参考:
https://stackoverflow.com/a/1227204/496445
https://stackoverflow.com/a/990436/496445
答案 1 :(得分:3)
Python有严重的线程问题。基本上,将线程添加到Python应用程序几乎总是无法使其更快,有时会使其变慢。
这是由于Global Interpreter Lock或GIL。
以下是blog post,其中包括有关该主题的讨论。
绕过此限制的一种方法是使用进程而不是线程; multiprocessing模块使这变得更容易。
答案 2 :(得分:0)
用C语言编写的Python库可以随意获取/释放全局解释器锁(GIL)。那些不使用Python对象的人可以释放GIL,以便其他线程可以查看,但我相信数学库一直使用Python对象,因此有效地将math.sin序列化。由于锁定/解锁是一种开销,因此Python线程比进程慢。