Python什么是处理多个线程的最佳方法

时间:2015-02-03 20:38:58

标签: python multithreading

由于我的scaper运行速度很慢(一次一页)所以我正在尝试使用线程使其更快地运行。我有一个函数scrape(网站),它可以在网站上进行抓取,因此我可以轻松地创建每个线程并在每个线程上调用start()。

现在我想实现一个num_threads变量,它是我想要同时运行的线程数。处理这些多线程的最佳方法是什么?

对于ex:假设num_threads = 5,我的目标是启动5个线程然后抓住列表中的前5个网站并刮掉它们,然后如果线程#3完成,它将从列表中抓取第6个网站以立即刮掉,不要等到其他线程结束。

有关如何处理它的任何建议?谢谢

2 个答案:

答案 0 :(得分:0)

取决于。

如果您的代码花费大部分时间等待网络操作(可能在网络抓取应用程序中),则线程是合适的。实现线程池的最佳方法是在3.4中使用concurrent.futures。如果做不到这一点,你可以创建一个threading.Queue对象,并将每个线程编写为一个无限循环,从队列中消耗工作对象并处理它们。

如果您的代码在下载后花费大部分时间处理数据,则由于GIL,线程无用。 concurrent.futures提供对进程并发性的支持,但同样仅适用于3.4+。对于较旧的蟒蛇,请使用multiprocessing。它提供Pool类型,简化了创建进程池的过程。

您应该对您的代码进行分析(使用cProfile)来确定您遇到的这两种方案中的哪一种。

答案 1 :(得分:0)

如果您使用的是Python 3,请查看concurrent.futures.ThreadPoolExecutor

从文档ThreadPoolExecutor Example中提取示例:

import concurrent.futures
import urllib.request

URLS = ['http://www.foxnews.com/',
        'http://www.cnn.com/',
        'http://europe.wsj.com/',
        'http://www.bbc.co.uk/',
        'http://some-made-up-domain.com/']

# Retrieve a single page and report the url and contents
def load_url(url, timeout):
    conn = urllib.request.urlopen(url, timeout=timeout)
    return conn.readall()

# We can use a with statement to ensure threads are cleaned up promptly
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
    # Start the load operations and mark each future with its URL
    future_to_url = {executor.submit(load_url, url, 60): url for url in URLS}
    for future in concurrent.futures.as_completed(future_to_url):
        url = future_to_url[future]
        try:
            data = future.result()
        except Exception as exc:
            print('%r generated an exception: %s' % (url, exc))
        else:
            print('%r page is %d bytes' % (url, len(data)))

如果您正在使用Python 2,则可以使用backport:

ThreadPoolExecutor Example

from concurrent import futures
import urllib.request

URLS = ['http://www.foxnews.com/',
        'http://www.cnn.com/',
        'http://europe.wsj.com/',
        'http://www.bbc.co.uk/',
        'http://some-made-up-domain.com/']

def load_url(url, timeout):
    return urllib.request.urlopen(url, timeout=timeout).read()

with futures.ThreadPoolExecutor(max_workers=5) as executor:
    future_to_url = dict((executor.submit(load_url, url, 60), url)
                         for url in URLS)

    for future in futures.as_completed(future_to_url):
        url = future_to_url[future]
        if future.exception() is not None:
            print('%r generated an exception: %s' % (url,
                                                     future.exception()))
        else:
            print('%r page is %d bytes' % (url, len(future.result())))