【问题标题】:Resource usage of "time.sleep" in loop vs. "threading.Timer"循环中“time.sleep”与“threading.Timer”的资源使用情况
【发布时间】:2012-11-07 00:33:17
【问题描述】:

第一种方法:

import threading
import time

def keepalive():
    while True:
        print 'Alive.'
        time.sleep(200)
threading.Thread(target=keepalive).start()

第二种方法:

import threading

def keepalive():
    print 'Alive.'
    threading.Timer(200, keepalive).start()

threading.Timer(200, keepalive).start()

哪种方法占用更多内存?而在第二种方法中,线程被激活后是否结束?或者它是否保留在内存中并开始一个新线程? (多线程)

【问题讨论】:

  • 我把标题稍微通用了一些——对于这种特定的“基准”或“性能问题”,一个很好的初步答案是对其进行分析;显示“意外结果”的个人资料是更好问题的起点。
  • 你试过运行它吗?您可以使用顶部/活动监视器/ProgMan/等。查看内存使用情况和线程数,或者您可以在内部进行(例如,threading.active_count()),这可能比您提出合理问题的时间要短。
  • lop 和 sleep() 调用有什么问题?为什么每次都开始一个新的线程?

标签: python multithreading memory timer python-2.7


【解决方案1】:

Timer 为每个启动的计时器创建一个新的线程对象,因此在创建和垃圾收集这些对象时肯定需要更多资源。

由于每个线程在产生另一个active_count 后立即退出,所以active_count 保持不变,但不断有新线程创建和销毁,这会导致开销。我会说第一种方法肯定更好。

虽然你不会真正看到太大的差异,只有当间隔非常小时。

【讨论】:

  • 这正是我一直在想的。尽管如此,每种方法的内存使用情况似乎几乎相同。当我使用第二种方法时,我的低端 Ubuntu 服务器 OOM-killer 一直在杀死我的程序。
【解决方案2】:

这是一个如何自己测试的示例:

而在第二种方法中,线程被激活后是否结束?或者它是否保留在内存中并开始一个新线程? (多线程)

import threading

def keepalive():
    print 'Alive.'
    threading.Timer(200, keepalive).start()
    print threading.active_count()

threading.Timer(200, keepalive).start()

我还将 200 更改为 .2,这样就不会花很长时间了。

线程数永远是 3。

然后我这样做了:

top -pid 24767

#TH 列从未改变。

所以,这就是你的答案:我们没有足够的信息来知道 Python 是为所有计时器维护一个计时器线程,还是在计时器运行后立即结束并清理线程,但我们可以确定线程不会粘在周围并堆积起来。 (如果您确实想知道发生了哪种情况,例如,可以打印线程 ID。)

另一种查找方法是查看来源。正如the documentation 所说,“Timer 是 Thread 的子类,因此也可以用作创建自定义线程的示例”。它是Thread 的子类这一事实已经告诉您每个Timer 都是Thread。它“作为一个例子”这一事实意味着它应该易于阅读。如果您单击文档中的链接到the source,您可以看到它是多么的微不足道。大部分工作都是由Event 完成的,但它们在同一个源文件中,而且几乎一样简单。实际上,它只是创建一个条件变量,等待它(因此它会阻塞直到超时,或者您通过调用cancel 通知条件),然后退出。

我之所以回答一个子问题并解释我是如何做到的,而不是回答每个子问题,是因为我认为执行相同的步骤会更有用。

进一步思考,这可能不是首先由优化决定的问题:

如果您有一个简单的同步程序,需要在 200 秒内什么都不做,请阻塞调用 sleep。或者,更简单,只需完成工作并退出,然后选择一个外部工具来安排您的脚本每 200 秒运行一次。

另一方面,如果你的程序本质上是异步的——尤其是如果你已经有了线程、信号处理程序和/或事件循环——那么你就不可能让sleep 工作。如果Timer 效率太低,请转到 PyPI 或 ActiveState 并找到更好的计时器,让您可以使用单个实例和线程安排可重复的计时器(甚至多个计时器)。 (或者,如果您使用信号,请使用 signal.alarmsetitimer,如果您使用事件循环,请将计时器构建到您的主循环中。)

我想不出任何用例让sleepTimer 都成为有力的竞争者。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多