【问题标题】:Python what is the best way to handle multiple threadsPython处理多线程的最佳方法是什么
【发布时间】:2015-02-03 20:38:58
【问题描述】:

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

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

例如:假设 num_threads = 5 ,我的目标是启动 5 个线程,然后抓取列表中的前 5 个网站并抓取它们,然后如果线程 #3 完成,它将立即抓取列表中的第 6 个网站进行抓取,而不是等到其他线程结束。

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

【问题讨论】:

  • 也许这有帮助? stackoverflow.com/questions/2846653/… 但我不确定你真的需要线程吗?只是某种队列?
  • 您是否考虑过使用Scrapy
  • @trainoasis 是的,它就像队列,5 个线程将从该队列中提取数据以执行它们

标签: python multithreading


【解决方案1】:

视情况而定。

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

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

您应该分析您的代码(使用cProfile)以确定您遇到的这两种情况中的哪一种。

【讨论】:

  • 谢谢,我大部分时间都花在我的程序上进行抓取,数据将保存到文本文件中,稍后将由另一个脚本使用
【解决方案2】:

如果您使用的是 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,则有一个可用的反向端口:

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())))

【讨论】:

  • 有一个可用的反向端口,我编辑了我的答案以包含它
猜你喜欢
  • 1970-01-01
  • 2016-12-21
  • 1970-01-01
  • 2017-03-07
  • 2016-05-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多