对于您的主要问题:
如果你正在做比这更复杂的事情——或者,特别是,如果你重复这样做——你可能需要一个“线程组”类。有几十个是预制的,但如果你不喜欢其中任何一个,自己写一个就很简单了。
然后,而不是这个:
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 个作业,而无需维护队列等所有工作。缺点是您不能只迭代线程要获得返回值,您必须迭代作业——理想情况下,您不想让作业一直存活到最后,这样您就可以迭代它们。
这将我们带到您的第二个问题,即如何从线程(或作业)中获取值。有很多很多方法可以做到这一点。
你所做的工作。你甚至不需要任何锁定。
正如您所建议的,使用回调也可以。但请记住,回调将在工作线程上运行,而不是在主线程上,所以如果它正在访问某个全局对象,您将需要某种同步。
如果您无论如何都要同步,那么回调可能没有任何好处。例如,如果你想要做的只是对一堆值求和,你可以只 set total=[0]
,并让每个线程total[0] += myValue
在锁内执行。(当然在这种情况下,只在主线程中进行求和并避免锁定可能更有意义,但如果合并结果的工作量更大,那么这个选择可能就不那么简单了。)
您还可以使用某种原子对象,而不是显式锁定。例如,标准的 Queue.Queue 和 collections.deque 都是原子的,所以每个线程可以只 set q = Queue.Queue()
,然后每个线程通过 do 推送它的结果q.push(myValue)
,然后加入后你只需迭代并总结队列的值。
事实上,如果每个线程只向队列推送一次,您可以在队列本身上执行 10 次阻塞获取,之后您知道group.join()
orpool.wait()
或 what 将快速返回。
或者您甚至可以将回调作为作业推送到队列中。同样,您可以在队列上执行 10 次阻塞获取,每次都执行结果。
如果每个线程可以返回多个对象,他们可以在完成后将一个标记值或回调推送到队列中,并且您的主线程会不断弹出,直到它读取 10 个标记。