我是python的新手并且在threading
上取得了一些进展 - 我正在进行一些音乐文件转换,并希望能够在我的机器上使用多个核心(每个核心一个活动的转换线程)。
class EncodeThread(threading.Thread):
# this is hacked together a bit, but should give you an idea
def run(self):
decode = subprocess.Popen(["flac","--decode","--stdout",self.src],
stdout=subprocess.PIPE)
encode = subprocess.Popen(["lame","--quiet","-",self.dest],
stdin=decode.stdout)
encode.communicate()
# some other code puts these threads with various src/dest pairs in a list
for proc in threads: # `threads` is my list of `threading.Thread` objects
proc.start()
一切正常,所有文件都被编码,勇敢! ...但是,所有进程立即生成,但我只想一次运行两个(每个核心一个)。一旦完成,我希望它继续到列表中的下一个,直到它完成,然后继续该程序。
我该怎么做?
(我查看了线程池和队列函数,但我找不到简单的答案。)
编辑:也许我应该添加我的每个线程都使用subprocess.Popen
运行单独的命令行 decoder (flac)管道传输到stdout,这是输入命令行 encoder (lame / mp3)。
答案 0 :(得分:33)
如果要限制并行线程数,请使用semaphore:
threadLimiter = threading.BoundedSemaphore(maximumNumberOfThreads)
class EncodeThread(threading.Thread):
def run(self):
threadLimiter.acquire()
try:
<your code here>
finally:
threadLimiter.release()
立即启动所有主题。 maximumNumberOfThreads
以外的所有内容都将在threadLimiter.acquire()
中等待,等待线程只会在另一个线程通过threadLimiter.release()
后继续。
答案 1 :(得分:4)
“我的每个线程都使用subprocess.Popen
来运行单独的命令行[process]”。
为什么有一堆线程管理一堆进程?这正是操作系统为您做的。为什么要微观管理操作系统已经管理的内容?
而不是愚弄监督进程的线程,而不是分离进程。您的进程表可能无法处理2000个进程,但它可以很容易地处理几十个(可能是几百个)。
您希望更多工作,而不是您的CPU可能处理排队。真正的问题是内存之一 - 而不是进程或线程。如果所有进程的所有活动数据的总和超过物理内存,则必须交换数据,这会降低您的速度。
如果您的进程占用的内存非常小,那么您可以运行很多批次。如果您的进程占用大量内存,则无法运行很多。
答案 2 :(得分:1)
如果您使用默认的“cpython”版本,那么这对您没有帮助,因为一次只能执行一个线程;查找Global Interpreter Lock。相反,我建议在Python 2.6中查看multiprocessing
module - 它使并行编程变得简单。您可以使用Pool
进程创建2*num_threads
对象,并为其执行一系列任务。它将一次执行最多2*num_threads
个任务,直到完成所有任务。
在工作中,我最近迁移了一堆Python XML工具(一个不同的,xpath grepper和bulk xslt转换器)来使用它,并且每个处理器有两个进程,效果非常好。
答案 3 :(得分:1)
在我看来,您想要的是某种类型的池,并且在该池中您希望有n个线程,其中n ==系统上的处理器数量。然后,您将拥有另一个线程,其唯一的工作是将作业提供到队列中,工作线程可以在它们变为空闲时接收和处理(因此对于双代码机器,您有三个线程,但主线程将执行很少)。
虽然您不熟悉Python,但我会假设您不了解GIL及其在线程方面的副作用。如果您阅读我链接的文章,您很快就会明白为什么传统的多线程解决方案并不总是Python世界中最好的。相反,你应该考虑使用multiprocessing模块(Python 2.6中的新功能,在2.5中你可以use this backport)来实现相同的效果。它通过使用多个进程来解决GIL的问题,就好像它们是同一个应用程序中的线程一样。关于如何共享数据(你在不同的内存空间中工作)存在一些限制,但实际上这并不是坏事:它们只是鼓励良好的做法,例如最小化线程之间的联系点(或者在这种情况下的进程)。
在您的情况下,您可能有兴趣使用指定的here池。
答案 4 :(得分:1)
简答:不要使用线程。
对于一个工作示例,您可以查看我最近在工作中共同抛出的内容。它是ssh
的一个小包装器,它运行可配置数量的Popen()
个子进程。我发布在:Bitbucket: classh (Cluster Admin's ssh Wrapper)。
如上所述,我不使用线程;我刚刚生成了孩子,循环调用他们的.poll()
方法并检查超时(也是可配置的)并在收集结果时补充池。我玩过不同的sleep()
值,过去我写过一个版本(在 subprocess 模块添加到Python之前),它使用了信号模块(SIGCHLD和SIGALRM)和 os.fork()和 os.execve()函数---我的管道和文件描述符管道等等。) / p>
在我的情况下,当我收集结果时,我会逐步打印结果...并记住所有这些结果总结(当所有作业都已完成或因超过超时而被杀死时)。
我在25,000个内部主机(其中许多已关闭,已退休,位于国际,我的测试帐户无法访问等)的列表中运行了该内容。它在两个多小时内完成了这项工作,没有任何问题。 (由于系统处于退化/颠簸状态,大约有60个超时 - 证明我的超时处理工作正常。)
所以我知道这个模型可靠地工作。使用此代码运行100个当前ssh
进程似乎不会产生任何明显的影响。 (这是一个中等老旧的FreeBSD盒子)。我以前在旧的512MB笔记本电脑上运行旧的(前子进程)版本,并且有100个并发进程,但也没有问题。
(顺便说一句:我打算清理它并添加功能;随时贡献或克隆自己的分支;这就是Bitbucket.org的用途)。
答案 5 :(得分:0)
我不是这方面的专家,但我读过一些关于“锁定”的内容。 This article可能会帮助您
希望这有帮助
答案 6 :(得分:0)
我想添加一些内容,以作为其他希望做类似事情的参考,但他们可能编码的内容与OP不同。这个问题是我在搜索时遇到的第一个问题,选择的答案为我指明了正确的方向。只是想把东西还给我。
import threading
import time
maximumNumberOfThreads = 2
threadLimiter = threading.BoundedSemaphore(maximumNumberOfThreads)
def simulateThread(a,b):
threadLimiter.acquire()
try:
#do some stuff
c = a + b
print('a + b = ',c)
time.sleep(3)
except NameError: # Or some other type of error
# in case of exception, release
print('some error')
threadLimiter.release()
finally:
# if everything completes without error, release
threadLimiter.release()
threads = []
sample = [1,2,3,4,5,6,7,8,9]
for i in range(len(sample)):
thread = threading.Thread(target=(simulateThread),args=(sample[i],2))
thread.daemon = True
threads.append(thread)
thread.start()
for thread in threads:
thread.join()
这基本上遵循您在此站点上找到的内容: https://www.kite.com/python/docs/threading.BoundedSemaphore