在python中使用多处理的策略

时间:2011-09-24 19:36:41

标签: python multithreading multiprocessing

我对多处理完全陌生。我一直在阅读有关多处理模块的文档。我读到了关于池,线程,队列等的内容,但我完全迷失了。

我想要做的多处理是,转换我简陋的http下载器,与多个工作人员合作。我现在正在做的是,下载一个页面,解析到页面以获得有趣的链接。继续,直到下载所有有趣的链接。现在,我想用多处理来实现它。但我现在不知道如何组织这个工作流程。我有两个想法。首先,我想过有两个队列。需要下载的链接的一个队列,其他用于要解析的链接的队列。一名工作人员下载页面,并将其添加到队列中,该队列用于需要解析的项目。其他进程解析一个页面,并将它感兴趣的链接添加到另一个队列。我期望从这种方法出现的问题是:首先,为什么一次下载一页并一次解析页面。此外,一个进程如何知道在队列中的所有项目耗尽之后,有些项目将被添加到队列中。

我想要使用的另一种方法是。有一个函数,可以用url作为参数调用。此函数下载文档并开始解析链接。每次遇到一个有趣的链接时,它会立即创建一个与自身运行相同功能的新线程。我对这种方法的问题是,我如何跟踪周围产生的所有进程,如何知道是否还有进程要运行。而且,我如何限制最大进程数。

所以我完全迷失了。任何人都可以建议一个好的策略,并且可能会显示一些关于如何实现这个想法的示例代码。

2 个答案:

答案 0 :(得分:3)

这是一种使用多处理的方法。 (非常感谢@Voo,建议对代码进行许多改进)。

import multiprocessing as mp
import logging
import Queue
import time

logger=mp.log_to_stderr(logging.DEBUG)  # or, 
# logger=mp.log_to_stderr(logging.WARN) # uncomment this to silence debug and info messages

def worker(url_queue,seen):
    while True:
        url=url_queue.get()
        if url not in seen:
            logger.info('downloading {u}'.format(u=url))
            seen[url]=True
            # Replace this with code to dowload url
            # urllib2.open(...)
            time.sleep(0.5)
            content=url
            logger.debug('parsing {c}'.format(c=content))
            # replace this with code that finds interesting links and
            # puts them in url_queue
            for i in range(3):
                if content<5:
                    u=2*content+i-1
                    logger.debug('adding {u} to url_queue'.format(u=u))
                    time.sleep(0.5)
                    url_queue.put(u)
        else:
            logger.debug('skipping {u}; seen before'.format(u=url))
        url_queue.task_done()

if __name__=='__main__':
    num_workers=4
    url_queue=mp.JoinableQueue()
    manager=mp.Manager()
    seen=manager.dict()

    # prime the url queue with at least one url
    url_queue.put(1)
    downloaders=[mp.Process(target=worker,args=(url_queue,seen))
                 for i in range(num_workers)]
    for p in downloaders:
        p.daemon=True
        p.start()
    url_queue.join()
  • 创建(4)工作进程池。
  • JoinableQueue,名为url_queue
  • 每个工作人员从url_queue获取一个网址,找到新的网址并添加 他们到url_queue
  • 仅在添加新项目后才会调用url_queue.task_done()
  • 主要流程调用url_queue.join()。这阻止了主要 直到task_done为每个任务调用的过程 url_queue
  • 由于工作进程将daemon属性设置为True, 当主要过程结束时,它们也会结束。

此示例中使用的所有组件也在Doug Hellman's excellent Python Module of the Week tutorial on multiprocessing中解释。

答案 1 :(得分:1)

你所描述的基本上是图遍历;大多数图形遍历算法(比首先深度更复杂),跟踪两个节点集,在您的情况下,节点是url。

第一组称为“闭集”,表示已经访问和处理过的所有节点。如果,当您正在处理页面时,您发现恰好位于封闭集中的链接,您可以忽略它,它已被处理。

第二组不出所料地称为“开放集”,包括已找到但尚未处理的所有边。

基本机制是首先将根节点放入开放集(封闭集最初为空,尚未处理任何节点),然后开始工作。每个工作者从打开的集合中获取单个节点,将其复制到关闭的集合,处理节点,并将它发现的任何节点添加回打开的集合(只要它们不在已打开或关闭的集合中) 。一旦open set为空,(并且没有工作人员仍在处理节点),图表已经完全遍历。

实际上在multiprocessing中实现这一点可能意味着你将拥有一个跟踪开放和封闭集合的主任务;如果工作池中的工作程序指示它已准备好工作,则主工作人员负责将节点从打开集移动到关闭集并启动工作程序。然后,工作人员可以通过所有他们找到的节点,而不必担心他们是否已经关闭,然后回到主人那里;并且主服务器将忽略已经关闭的节点。