Python urllib2的多处理无用?

Python urllib2的多处理无用?,python,multiprocessing,urllib2,gil,Python,Multiprocessing,Urllib2,Gil,我最近尝试使用多处理模块(及其工作池)来加速一个小工具(它使用urllib2向(非官方的)twitter按钮计数url(>2000个url)发送请求,并解析其结果)。我在这里读了几篇关于多线程(与标准的非线程版本相比,它减慢了整个过程)和多处理的讨论,但我找不到一个(可能非常简单)问题的答案: 你能通过多处理来加速url调用吗?瓶颈不是网络适配器吗?例如,我不知道urllib2 open方法的哪一部分可以并行化,以及该如何工作 编辑:这是我想要加速的请求和当前的多处理设置: urls=["ww

我最近尝试使用多处理模块(及其工作池)来加速一个小工具(它使用urllib2向(非官方的)twitter按钮计数url(>2000个url)发送请求,并解析其结果)。我在这里读了几篇关于多线程(与标准的非线程版本相比,它减慢了整个过程)和多处理的讨论,但我找不到一个(可能非常简单)问题的答案:

你能通过多处理来加速url调用吗?瓶颈不是网络适配器吗?例如,我不知道urllib2 open方法的哪一部分可以并行化,以及该如何工作

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

 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]

啊,又来了一次关于GIL的讨论。事情是这样的。使用urllib2获取内容主要是IO绑定的。当任务绑定到IO时,本机线程和多处理都具有相同的性能(线程仅在绑定到CPU时才成为问题)。是的,你可以加快速度,我自己用python线程和10个下载线程来完成

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

下面是一些伪代码:

# 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),并且一个请求完全占用了带宽,那么运行多个下载是毫无意义的。您运行多个下载(通常)的原因是请求很小,并且具有相对较高的延迟/开销。

这取决于!您是否正在联系不同的服务器,传输的文件是大是小,您是否在等待服务器回复或传输数据时浪费了大量时间

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

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


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

当您将web请求拆分到多个进程时,您要做的是并行化网络延迟(即等待响应)。因此,您通常应该获得良好的加速,因为大多数进程在等待事件的大部分时间都应该处于睡眠状态


或者使用扭曲的

看一看,特别是这个例子:。它将比多处理和多线程处理快得多,而且它可以轻松处理数千个连接。

如果您的代码被破坏,没有什么有用的:
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方面)。您可以使用线程:将
多处理.Pool
替换为
多处理.Pool.ThreadPool
——其余部分相同。GIL是在CPython的IO期间释放的,因此如果大部分时间都花在
urlopen().read()
中,线程应该会加快代码的速度


这是一个。

Thx,我从工具和我的多处理“实现”中添加了一些代码,这并没有真正加快速度(顺便说一句,请求非常小),您是否考虑过可能会受到限制?这个API可能只允许每个IP同时连接一定数量的连接(或者可能是每分钟最大请求数)。是的,这是一个很好的观点。这只是一个例子(可能)是重用连接的一个很好的解决方案)编辑:使用30个进程,可以显著提高速度(如果我的测量没有因为tue连接限制、缓存不真实的东西等而混淆/偏差)30个进程有多少个处理器?@dorvak