【问题标题】:How to reduce memory usage of threaded python code?如何减少线程 python 代码的内存使用?
【发布时间】:2012-02-06 11:22:23
【问题描述】:

我编写了大约 50 个类,用于使用机械化和线程连接和处理网站。它们都同时工作,但它们并不相互依赖。这意味着 1 个课程 - 1 个网站 - 1 个线程。这不是特别优雅的解决方案,特别是对于管理代码,因为很多代码在每个类中重复(但不足以使其成为一个类来传递参数,因为某些站点可能需要在方法中间对检索到的数据进行额外处理- 像“登录” - 其他人可能不需要)。正如我所说,它并不优雅——但它确实有效。不用说,我欢迎所有关于如何在不为每个网站方法使用 1 个类的情况下更好地编写它的建议。为每个类添加额外的功能或整体代码管理是一项艰巨的任务。

但是,我发现每个线程占用大约 8MB 内存,因此在运行 50 个线程的情况下,我们正在查看大约 400MB 的使用量。如果它在我的系统上运行,我不会有问题,但由于它在只有 1GB 内存的 VPS 上运行,它开始成为一个问题。你能告诉我如何减少内存使用,或者有没有其他方法可以同时处理多个站点?

我使用这个快速测试 python 程序来测试是否是存储在我的应用程序变量中的数据正在使用内存,或者其他什么。正如你在下面的代码中看到的,它只处理 sleep() 函数,但每个线程都使用 8MB 内存。

from thread import start_new_thread
from time import sleep

def sleeper():
    try:
        while 1:
            sleep(10000)
    except:
        if running: raise

def test():
    global running
    n = 0
    running = True
    try:
        while 1:
            start_new_thread(sleeper, ())
            n += 1
            if not (n % 50):
                print n
    except Exception, e:
        running = False
        print 'Exception raised:', e
    print 'Biggest number of threads:', n

if __name__ == '__main__':
    test()

当我运行它时,输出是:

50
100
150
Exception raised: can't start new thread
Biggest number of threads: 188

通过删除running = False 行,我可以在shell 中使用free -m 命令测量空闲内存:

             total       used       free     shared    buffers     cached
Mem:          1536       1533          2          0          0          0
-/+ buffers/cache:       1533          2
Swap:            0          0          0

我知道每个线程大约需要 8MB 的实际计算很简单,方法是将上述测试应用程序运行之前和期间使用的内存的差异除以它设法启动的最大线程数。

这可能只是分配的内存,因为通过查看top,python 进程只使用了大约 0.6% 的内存。

【问题讨论】:

  • 什么占用了内存?我冒昧地猜测这是您从网站中提取的数据。如果是这种情况,那么除了限制执行线程的数量之外,您可能无能为力。
  • 您如何准确测量内存使用情况?我猜,那些 8MB 并没有真正分配给每个线程。这 8MB 的很大一部分可能在线程之间共享(只是猜测..)?
  • 这是主机吗? ulimit -u 呢?和ulimit -a?
  • @Andrew:所以,您粗略地测量了 python 中单个线程的开销。毕竟,现在 8MB 听起来很合理......
  • @andrew, ulimit -s 可以固定,limit pam module 它们是每个参数的软值和硬值。您还可以在 bash.rc 中为每个用户分配自定义限制。例如查看 Oracle 文档,Oracle server needs to customize this parameters to work properly

标签: python multithreading memory-management


【解决方案1】:

【讨论】:

  • 这个。如果资源管理是一个问题,只需拥有一个线程池并调整池限制。
  • 谢谢!看起来 Gevent 是我一直在寻找的。​​span>
【解决方案2】:

对于许多用例来说,使用“每个请求一个线程”是可以且容易的。但是,这将需要大量资源(如您所见)。

更好的方法是使用异步方法,但不幸的是它要复杂得多。

对这个方向的一些提示:

【讨论】:

  • 谢谢,非常感谢。我以前读过 Twisted,但遗憾的是我对它了解不多,而且从它的外观上看,我无法使用 mechanize 与它。我会看看我是否可以使用 asyncore 使 mechanize 工作。
  • 毕竟,“完美”的解决方案是混合使用线程池,每个 CPU 核心有一个线程(用于处理任务)和异步 IO。实际的解决方案将取决于您的实际应用程序代码。也许,即使是基于select 的简单解决方案也可以为您解决问题。
  • 这意味着:在你的线程中:发送一堆请求,然后进入一个循环,该循环将在适当的套接字上select,并一个接一个地处理任何传入的数据......等等。毕竟,无论如何,操作系统都关心套接字 IO,您的任务是以最有效的方式与操作系统交互。
  • 事情是,我的代码真的很简单。每个子类都是相同的,只是不同的 URL、不同的名称、值等,有时还有一些不同的数据处理方式。它们完全不相互依赖。我想要的只是同时运行它们,等待它们完成工作然后退出。我阅读的所有解决方案都是针对我认为更复杂的事情。我不敢相信有人没有开发一个模块来简单地异步/线程执行完全不相互依赖的类或函数。
  • @Andrew:所有必需的代码和框架都已存在,您只需立即使用它;)
【解决方案3】:

解决办法是这样替换代码:

1) 做点什么。
2) 等待某事发生。
3) 做点别的。

使用这样的代码:

1) 做点什么。
2) 安排它,以便在发生某些事情时完成其他事情。
3) 完成。

在其他地方,您有几个线程可以执行此操作:

1) 等待任何事情发生。
2) 处理发生的一切。
3) 转到第 1 步。

在第一种情况下,如果您正在等待 50 件事发生,那么您有 50 个线程在等待 50 件事发生。在第二种情况下,您有一个线程在等待,它将执行需要完成的 50 件事情中的任何一件。

所以,不要使用线程来等待某件事发生。相反,应该安排它,以便当那件事发生时,其他一些线程将执行接下来需要完成的任何事情。

【讨论】:

    【解决方案4】:

    我不是 Python 方面的专家,但可能有一些线程池控制活动线程的总数,并在前一个线程完成后将“请求”移交给线程。请求不必是完整的线程对象,只要有足够的数据来完成任何请求即可。

    您也可以构建它,让线程池 A 有 N 个线程 ping 网站,一旦检索到数据,将数据交给线程池 B,Y 个线程处理数据。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-03-30
      • 1970-01-01
      • 1970-01-01
      • 2012-06-15
      • 1970-01-01
      • 1970-01-01
      • 2016-05-22
      相关资源
      最近更新 更多