如何管理python线程结果?

时间:2010-07-13 17:15:36

标签: python multithreading arrays

我正在使用此代码:

def startThreads(arrayofkeywords):
    global i
    i = 0
    while len(arrayofkeywords):
        try:
            if i<maxThreads:
                keyword = arrayofkeywords.pop(0)
                i = i+1
                thread = doStuffWith(keyword)
                thread.start()
        except KeyboardInterrupt:
            sys.exit()
    thread.join()

对于python中的线程,我几乎已经完成了所有事情,但我不知道如何管理每个线程的结果,在每个线程上我都有一个字符串数组作为结果,如何安全地将所有这些数组加入一个?因为,如果我尝试写入全局数组,两个线程可能同时写入。

6 个答案:

答案 0 :(得分:14)

使用Queue.Queue实例,它本质上是线程安全的。每个线程在完成后可以.put将其结果发送到该全局实例,并且主线程(当它知道所有工作线程完成时,通过.join例如在@unholysampler的答案中)可以循环.get从中获取每个结果,并将每个结果用于.extend“整体结果”列表,直到队列清空为止。

编辑:您的代码还有其他大问题 - 如果最大线程数小于关键字数量,它将永远不会终止(您尝试启动一个线程)关键字 - 永远不会少 - 但如果您已经启动了最大数字,那么您将永远循环到无其他目的。)

请考虑使用线程池,有点像this recipe中的那个,除了代替排队callables你将排队关键字 - 因为你想要的可调用在线程中运行在每个线程中是相同的,只是改变参数。当然,可调用的内容将被更改为在传入任务队列(使用.get)和结果列表.put时将某些内容剥离到传出结果队列中。

要终止N个线程,您可以在所有关键字之后.put N个“哨兵”(例如None,假设没有关键字可以是None):线程的可调用将退出它刚刚提取的“关键字”是None

Queue.Queue通常提供了在Python中组织线程(和多处理!)体系结构的最佳方法,就像我指向的配方一样,或者像我建议的那样更专业你在最后两段中的用例。

答案 1 :(得分:13)

首先,您确实需要保存所有这些thread个对象,以便在其上调用join()。如上所述,您只保存最后一个,然后仅保存没有异常。

执行多线程编程的一种简单方法是为每个线程提供运行所需的所有数据,然后让它不写入该工作集之外的任何内容。如果所有线程都遵循该指南,则它们的写入不会相互干扰。然后,一旦线程完成,只让主线程将结果聚合到一个全局数组中。这被称为“fork / join parallelism。”

如果您继承Thread对象,则可以为其存储该返回值的空间,而不会干扰其他线程。然后你可以做这样的事情:

class MyThread(threading.Thread):
    def __init__(self, ...):
        self.result = []
        ...

def main():
    # doStuffWith() returns a MyThread instance
    threads = [ doStuffWith(k).start() for k in arrayofkeywords[:maxThreads] ]
    for t in threads:
        t.join()
        ret = t.result
        # process return value here

修改

看了一下后,看起来像上面的方法isn't the preferred way to do threads in Python。以上是线程的Java-esque模式。相反,你可以做类似的事情:

def handler(outList)
    ...
    # Modify existing object (important!)
    outList.append(1)
    ...

def doStuffWith(keyword):
    ...
    result = []
    thread = Thread(target=handler, args=(result,))
    return (thread, result)

def main():
    threads = [ doStuffWith(k) for k in arrayofkeywords[:maxThreads] ]
    for t in threads:
        t[0].start()
    for t in threads:
        t[0].join()
        ret = t[1]
        # process return value here

答案 2 :(得分:3)

您需要保持指向您所创建的每个线程的指针。因此,您的代码仅确保最后创建的线程完成。这并不意味着你在它之前开始的所有那些也已经完成了。

def startThreads(arrayofkeywords):
    global i
    i = 0
    threads = []
    while len(arrayofkeywords):
        try:
            if i<maxThreads:
                keyword = arrayofkeywords.pop(0)
                i = i+1
                thread = doStuffWith(keyword)
                thread.start()
                threads.append(thread)
        except KeyboardInterrupt:
            sys.exit()
    for t in threads:
        t.join()
    //process results stored in each thread

这也解决了写访问的问题,因为每个线程都会在本地存储它的数据。然后在完成所有这些操作后,您可以完成组合每个线程本地数据的工作。

答案 3 :(得分:1)

我知道这个问题有点陈旧,但最好的办法是不要像其他同事提出的方式那样伤害自己太多:)

请阅读Pool上的参考资料。这样你就可以加入你的工作了:

def doStuffWith(keyword):
    return keyword + ' processed in thread'

def startThreads(arrayofkeywords):
    pool = Pool(processes=maxThreads)
    result = pool.map(doStuffWith, arrayofkeywords)
    print result

答案 4 :(得分:0)

如果使用信号量来保护关键部分,写入全局数组就没问题了。当你想要附加到全局数组时,你'获得'锁定,然后在你完成时'释放'。这样,每个只有一个线程附加到数组。

查看http://docs.python.org/library/threading.html并搜索信号量以获取更多信息。

sem = threading.Semaphore()
...
sem.acquire()
# do dangerous stuff
sem.release()

答案 5 :(得分:0)

尝试一些信号量的方法,比如获取和释放.. http://docs.python.org/library/threading.html