在Python中处理线程连接的正确方法

时间:2012-07-19 23:33:07

标签: python multithreading join spawn

因此我编写了一个工具列表,将其分成给定数量的列表(比如10),然后从10个线程中取出10个列表并生成“EvaluationThreads”(扩展threading.thread) ,并且每个线程都会评估它们提供给评估的内容。当我启动每个线程时,我将它们全部放入一个列表中,在产生它们之后,我有以下代码:

for th in threadList:
    th.join()
    someTotal = th.resultsAttribute

这就是我等待所有线程完成并收集信息的方法。虽然这是一种等待一切完成然后收集结果的工作方式,但我觉得必须有一种更优雅的方式来做这件事,因为这些线程可以很好地在不同的时间完成,如果第一个开始完成所有的之前完成的必须等待该线程完成才能加入。有没有办法获得这些线程的信息并在它们完成时加入它们而不是按照它们的启动顺序加入它们?我原本认为我会在线程或其他东西中使用某种回调,但我不确定是否有更可接受的解决方案。

感谢您的帮助。

编辑:为了澄清,我的评估函数不受CPU限制,我不是试图在线程中分发文档以尽快完成它,每个线程都有一个固定的偶数个作业。

2 个答案:

答案 0 :(得分:2)

主要问题:

如果你正在做一些比这更复杂的事情,或者特别是如果你反复这样做 - 你可能想要一个“线程组”类。有几十个预制,但如果你不喜欢其中任何一个,自己写一个是非常简单的。

然后,而不是这个:

threadList = []
for argchunk in splitIntoChunks(values, 10):
  threadList.append(threading.Thread(target=myThreadFunc, args=argchunk))
...
someTotal = 0
for th in threadList:
  th.join()
  someTotal += th.resultsAttribute

你可以这样做:

threadGroup = ThreadGroup.ThreadGroup()
for argchunk in splitIntoChunks(values, 10):
  threadGroup.newThread(myThreadFunc, argchunk)
threadGroup.join()
someTotal = sum(th.resultsAttribute for th in threadGroup)

或者甚至更好,一个完整的线程池库,所以你可以这样做:

pool = ThreadPool(10)
for argchunk in splitIntoChunks(values, 100):
  pool.putRequest(myThreadFunc, argchunk)
pool.wait()

这里的优点是你可以轻松地在10个线程上安排100个作业,而不是每个线程一个10个作业,而不需要维护队列的所有工作,等等。缺点是你不能只是迭代线程来获取返回值,你必须迭代作业 - 理想情况下,你不希望工作保持活着直到结束只是为了你可以迭代它们。

这将我们带到第二个问题,即如何从线程(或作业)中获取值。有许多方法可以做到这一点。

你做了什么工作。你甚至不需要任何锁定。

如您所建议的,使用回调也有效。但请记住,回调将在工作线程上运行,而不是在主线程上运行,因此如果它正在访问某个全局​​对象,则需要进行某种同步。

如果您要进行同步,则回调可能没有任何好处。例如,如果你要做的就是将一堆值相加,你可以设置total=[0],并让每个线程在锁内做total[0] += myValue。 (当然在这种情况下,在主线程中进行求和并避免锁定可能更有意义,但如果将结果合并的工作更加冗长,那么这种选择可能并不那么简单。)

您还可以使用某种原子对象,而不是显式锁定。例如,标准Queue.Queue和collections.deque都是原子的,所以每个线程都可以设置q = Queue.Queue(),然后每个线程通过执行q.push(myValue)推送其结果,然后在加入之后只需迭代和求和队列的值。

事实上,如果每个线程只推送一次队列,你就可以在队列本身上做10次阻塞,之后你知道group.join()pool.wait()或其他任何东西会返回快。

或者您甚至可以将回调作为作业推送到队列中。同样,您可以对队列执行10次阻塞,每次都执行结果。

如果每个线程都可以返回多个对象,那么当它们完成时,它们可以将一个sentinel值或回调推送到队列中,并且你的主线程会一直弹出,直到它读取10个标记。

答案 1 :(得分:1)

使用队列在线程可用后立即将其中的信息推出:

让我们说这是你的主题:

class myThread(threading.Thread):
   def __init__(self, results_queue):
       self.results_queue = results_queue
       #other init code here


   def run(self):
       #thread code here

       self.results_queue.put(result) #result is the information you want from the thread

这是你的主要代码:

import Queue #or "import queue" in Python 3.x
results_queue = Queue()

#thread init code here

for i in xrange(num_threads_running):
    data = results_queue.get() # queue.get() blocks until some item is available
    #process data as it is made available

#at this point, there is no need to .join(), since all the threads terminate as soon as they put data to the queue.