循环中“time.sleep”与“threading.Timer”的资源使用情况

时间:2012-11-07 00:33:17

标签: python multithreading memory timer python-2.7

第一种方法:

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

哪种方法占用更多内存?在第二种方法中,线程在被激活后结束了吗?或者它是否保留在内存中并启动新线程? (多线程)

2 个答案:

答案 0 :(得分:5)

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

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

尽管间隔非常小,但你不会真正看到太大的差异。

答案 1 :(得分:3)

以下是如何自行测试的示例:

  

在第二种方法中,线程在被激活后结束了吗?或者它是否保留在内存中并启动新线程? (多线程)

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都是严重竞争者的任何用例。