【问题标题】:python multithreading synchronizationpython多线程同步
【发布时间】:2011-11-09 03:18:33
【问题描述】:

我在使用 cPython 进行线程处理时遇到了同步问题。我有两个文件,我解析它们并返回所需的结果。但是,下面的代码表现得很奇怪,返回三次而不是两次,加号并没有按照我将它们放入队列的顺序返回。代码如下:

import Queue
import threading
from HtmlDoc import Document

OUT_LIST = []

class Threader(threading.Thread):
    """
    Start threading
    """
    def __init__(self, queue, out_queue):
        threading.Thread.__init__(self)
        self.queue = queue
        self.out_queue = out_queue


    def run(self):
        while True:
            if self.queue.qsize() == 0: break

            path, host = self.queue.get()

            f = open(path, "r")
            source = f.read()
            f.close()

            self.out_queue.put((source, host))           
            self.queue.task_done()



class Processor(threading.Thread):
    """
    Process threading
    """
    def __init__(self, out_queue):
        self.out_queue = out_queue
        self.l_first = []
        self.f_append = self.l_first.append
        self.l_second = []
        self.s_append = self.l_second.append
        threading.Thread.__init__(self)


    def first(self, doc):
        # some code to to retrieve the text desired, this works 100% I tested it manually

    def second(self, doc):
        # some code to to retrieve the text desired, this works 100% I tested it manually

    def run(self):
        while True:
            if self.out_queue.qsize() == 0: break

            doc, host = self.out_queue.get()

            if host == "first":
                self.first(doc)
            elif host == "second":
                self.second(doc)

            OUT_LIST.extend(self.l_first + self.l_second)

            self.out_queue.task_done()


def main():

    queue = Queue.Queue()
    out_queue = Queue.Queue()

    queue.put(("...first.html", "first"))
    queue.put(("...second.html", "second"))

    qsize = queue.qsize()

    for i in range(qsize):
        t = Threader(queue, out_queue)
        t.setDaemon(True)
        t.start()

    for i in range(qsize):
        dt = Processor(out_queue)
        dt.setDaemon(True)
        dt.start()

    queue.join()
    out_queue.join()

    print '<br />'.join(OUT_LIST)

main()

现在,当我打印时,我想先打印“第一”的内容,然后再打印“第二”的内容。谁能帮我?

注意:我正在使用线程,因为实际上我必须一次连接 10 多个地方并检索其结果。我相信线程是完成这样一个任务最合适的方式

【问题讨论】:

    标签: python multithreading synchronization


    【解决方案1】:

    我正在使用线程,因为实际上我必须一次连接 10 多个地方并检索其结果。我相信线程是完成这样一个任务最合适的方式

    线程实际上是管理多个并发连接的最容易出错的方法之一。更强大、更可调试的方法是使用事件驱动的异步网络,例如由Twisted 实现。如果您有兴趣使用此模型,您可能需要查看this introduction

    【讨论】:

      【解决方案2】:

      我不同意线程是执行此操作的最佳方式(IMO 某些事件/选择机制会更好),但您的代码问题可能出在变量 t 和 dt 中。您在循环中有分配,对象实例将存储在任何地方 - 因此您的线程/处理器的新实例可能会在每个循环结束时被删除。

      如果您向我们展示此代码的精确输出,会更清楚。

      【讨论】:

        【解决方案3】:

        1) 您无法控制作业完成的顺序。这取决于执行时间,因此要根据需要返回结果,您可以使用作业对象创建全局字典,例如 job_results : {'first' : None, 'second' : None} 并在此处存储结果,而不是您可以根据需要获取数据顺序

        2) self.firstself.second 应该在处理完每个文档后清除,否则您将在 OUT_LIST 中有重复项

        3) 您可以使用 subprocess 模块进行多处理,例如将所有结果数据放入 CSV 文件中,然后按照您的意愿对其进行排序。

        【讨论】:

        • 我更喜欢多进程而不是多线程,因为同步问题的过度杀伤力以及当您需要在多个服务器上运行脚本时可能具有更高的并行度。
        • I/O 绑定线程受 GIL 和上下文切换的影响要小得多。线程消耗的资源比进程少,在这种情况下,这似乎很好。