【问题标题】:Multiprocessing useless with urllib2?多处理对 urllib2 没用?
【发布时间】:2011-10-17 20:39:48
【问题描述】:

我最近尝试使用多处理模块(和它的工作人员池)。我在这里阅读了几个关于多线程(与标准的非线程版本相比减慢了整个过程)和多处理的讨论,但我找不到一个(可能非常简单的)问题的答案:

你能通过多处理加速 url 调用,还是不是像网络适配器这样的瓶颈?我没有看到例如 urllib2-open-method 的哪一部分可以并行化,以及它应该如何工作......

编辑:这是我想要加速的请求和当前的多处理设置:

 urls=["www.foo.bar", "www.bar.foo",...]
 tw_url='http://urls.api.twitter.com/1/urls/count.json?url=%s'

 def getTweets(self,urls):
    for i in urls:
        try:
            self.tw_que=urllib2.urlopen(tw_url %(i))
            self.jsons=json.loads(self.tw_que.read())
            self.tweets.append({'url':i,'date':today,'tweets':self.jsons['count']})
        except ValueError:
            print ....
            continue
    return self.tweets 

 if __name__ == '__main__':
    pool = multiprocessing.Pool(processes=4)            
    result = [pool.apply_async(getTweets(i,)) for i in urls]
    [i.get() for i in result]

【问题讨论】:

  • 编辑:使用 30 个进程,有一个显着的加速(如果我的措施没有混淆/偏见由于 tue 连接限制,缓存相关的东西等)
  • 30 个进程有多少个处理器? @dorvak

标签: python multiprocessing urllib2 gil


【解决方案1】:

啊,又是关于 GIL 的讨论。事情就是这样。使用 urllib2 获取内容将大部分受 IO 限制。当任务受 IO 限制时,本机线程和多处理将具有相同的性能(线程仅在受 CPU 限制时才会成为问题)。是的,您可以加快速度,我自己使用 python 线程和类似 10 个下载器线程完成了它。

基本上,您使用生产者-消费者模型,其中一个线程(或进程)生成要下载的 url,而 N 个线程(或进程)从该队列消费并向服务器发出请求。 p>

这是一些伪代码:

# Make sure that the queue is thread-safe!!

def producer(self):
    # Only need one producer, although you could have multiple
    with fh = open('urllist.txt', 'r'):
        for line in fh:
            self.queue.enqueue(line.strip())

def consumer(self):
    # Fire up N of these babies for some speed
    while True:
        url = self.queue.dequeue()
        dh = urllib2.urlopen(url)
        with fh = open('/dev/null', 'w'): # gotta put it somewhere
            fh.write(dh.read())

现在,如果您正在下载非常 块数据(数百 MB)并且单个请求完全使带宽饱和,那么运行多个下载是没有意义的。您运行多个下载的原因(通常)是因为请求很小并且具有相对较高的延迟/开销。

【讨论】:

  • 谢谢,我从工具中添加了一些代码以及我的多处理“实现”,这并没有真正让事情变得更快(请求非常小,顺便说一句)
  • 您是否考虑过您可能会受到限制?此 API 可能只允许每个 IP 一定数量的同时连接(或可能每分钟的最大请求数)。
  • 是的,这是一个很好的观点......这只是一个例子(也许code.google.com/p/urllib3)将是重用连接的一个很好的解决方案)
【解决方案2】:

看看gevent,特别是这个例子:concurrent_download.py。它比多处理和多线程要快得多 + 它可以轻松处理数千个连接。

【讨论】:

    【解决方案3】:

    这取决于!您是在联系不同的服务器,传输的文件是小是大,您是在等待服务器回复还是通过传输数据浪费了很多时间,...

    通常,多处理涉及一些开销,因此您希望确保通过并行化工作获得的加速比开销本身更大。

    另一点:网络和因此 I/O 绑定的应用程序使用异步 I/O 和事件驱动架构而不是线程或多处理可以更好地工作和扩展,因为在此类应用程序中,大部分时间都花在等待 I/O O 并且不做任何计算。

    对于您的具体问题,我会尝试使用TwistedgeventTornado 或任何其他不使用线程来并行化连接的网络框架来实现解决方案。

    【讨论】:

      【解决方案4】:

      当您将 Web 请求拆分到多个进程时,您所做的是并行化网络延迟(即等待响应)。所以你通常应该得到一个很好的加速,因为大多数进程应该大部分时间都在休眠,等待一个事件。

      或者使用 Twisted。 ;)

      【讨论】:

        【解决方案5】:

        如果您的代码被破坏,则没有任何用处:f()(带括号)立即调用 Python 中的函数,您应该只传递 f(不带括号)以在池中执行。您的问题代码:

        #XXX BROKEN, DO NOT USE
        result = [pool.apply_async(getTweets(i,)) for i in urls]
        [i.get() for i in result]
        

        注意getTweets后面的括号表示所有代码都在主线程中串行执行。

        将调用委托给池:

        all_tweets = pool.map(getTweets, urls)
        

        此外,您不需要单独的进程,除非 json.loads() 在您的情况下很昂贵(CPU 方面)。您可以使用线程:将multiprocessing.Pool 替换为multiprocessing.pool.ThreadPool - 其余部分相同。 GIL 在 CPython 的 IO 期间释放,因此如果大部分时间都花在 urlopen().read() 上,线程应该会加速你的代码。

        这是complete code example

        【讨论】:

          猜你喜欢
          • 2011-02-12
          • 2012-04-13
          • 2013-06-19
          • 1970-01-01
          • 2014-12-24
          • 1970-01-01
          • 1970-01-01
          • 2010-12-24
          • 1970-01-01
          相关资源
          最近更新 更多