我正在用Python监视一个文件,并在它达到一定大小时触发一个动作。现在我正在睡觉和轮询,但我相信有一个更优雅的方式来做到这一点:
POLLING_PERIOD = 10
SIZE_LIMIT = 1 * 1024 * 1024
while True:
sleep(POLLING_PERIOD)
if stat(file).st_size >= SIZE_LIMIT:
# do something
问题是,如果我有一个大的POLLING_PERIOD,如果文件增长快,我的文件限制是不准确的,但是如果我有一个小的POLLING_PERIOD,我浪费CPU。
谢谢!
我该怎么做?
谢谢!
Linux解决方案
你想看看如何使用pyinotify,它是inotify的Python绑定。
这是一个关于监视close
事件的例子,它不是一个大的跳转到监听大小变化。
#!/usr/bin/env python
import os, sys
from pyinotify import WatchManager, Notifier, ProcessEvent, EventsCodes
def Monitor(path):
class PClose(ProcessEvent):
def process_IN_CLOSE(self, event):
f = event.name and os.path.join(event.path, event.name) or event.path
print 'close event: ' + f
wm = WatchManager()
notifier = Notifier(wm, PClose())
wm.add_watch(path, EventsCodes.IN_CLOSE_WRITE|EventsCodes.IN_CLOSE_NOWRITE)
try:
while 1:
notifier.process_events()
if notifier.check_events():
notifier.read_events()
except KeyboardInterrupt:
notifier.stop()
return
if __name__ == '__main__':
try:
path = sys.argv[1]
except IndexError:
print 'use: %s dir' % sys.argv[0]
else:
Monitor(path)
<<p> Windows解决方案/strong> pywin32有Windows文件系统的文件系统通知绑定。
你想要寻找的是使用FindFirstChangeNotification并将其与FILE_NOTIFY_CHANGE_SIZE
的列表联系起来。这个例子监听文件名的变化——监听大小的变化并不是一个大的飞跃。
import os
import win32file
import win32event
import win32con
path_to_watch = os.path.abspath (".")
#
# FindFirstChangeNotification sets up a handle for watching
# file changes. The first parameter is the path to be
# watched; the second is a boolean indicating whether the
# directories underneath the one specified are to be watched;
# the third is a list of flags as to what kind of changes to
# watch for. We're just looking at file additions / deletions.
#
change_handle = win32file.FindFirstChangeNotification (
path_to_watch,
0,
win32con.FILE_NOTIFY_CHANGE_FILE_NAME
)
#
# Loop forever, listing any file changes. The WaitFor... will
# time out every half a second allowing for keyboard interrupts
# to terminate the loop.
#
try:
old_path_contents = dict ([(f, None) for f in os.listdir (path_to_watch)])
while 1:
result = win32event.WaitForSingleObject (change_handle, 500)
#
# If the WaitFor... returned because of a notification (as
# opposed to timing out or some error) then look for the
# changes in the directory contents.
#
if result == win32con.WAIT_OBJECT_0:
new_path_contents = dict ([(f, None) for f in os.listdir (path_to_watch)])
added = [f for f in new_path_contents if not f in old_path_contents]
deleted = [f for f in old_path_contents if not f in new_path_contents]
if added: print "Added: ", ", ".join (added)
if deleted: print "Deleted: ", ", ".join (deleted)
old_path_contents = new_path_contents
win32file.FindNextChangeNotification (change_handle)
finally:
win32file.FindCloseChangeNotification (change_handle)
<<p> OSX解决方案/strong> 也有使用PyKQueue进入OSX文件系统的等效钩子,但如果你能理解这些例子,你也可以谷歌OSX解决方案。
这是一篇关于跨平台文件系统监控的好文章。
你是正确的:"投票是邪恶的"。轮询越频繁,如果什么都没有发生,那么浪费的CPU就越多。如果轮询频率较低,则在事件发生时延迟处理。
然而,唯一的选择是"阻塞",直到你接收到某种"信号"。
如果你在Linux上,你可以使用"inotify":
http://linux.die.net/man/7/inotify您是对的,与其他完成某事的方法相比,轮询通常是次优解决方案。然而,有时这是最简单的解决方案,特别是当你试图编写一些同时在Windows和Linux/UNIX上运行的程序时。
幸运的是,现代硬件相当快。在我的机器上,我能够运行你的循环,每秒轮询10次,对Windows任务管理器中的CPU使用没有任何明显的影响,每秒100次确实会在使用图表上产生一些小的峰,CPU使用率偶尔会达到1%。
在您的示例中,轮询之间的10秒对于CPU使用来说是微不足道的。
你也可以给你的脚本一个较低的优先级,以确保它不会影响机器上其他任务的性能,如果这很重要的话。
有点笨拙,但是考虑到CPU负载与大小精度的难题,可变轮询周期是否合适?
简而言之,当文件大小接近极限和/或当前增长率高于一定水平时,轮询周期将减少。通过这种方式,至少在大多数情况下,轮询的CPU负载成本会有所降低。
例如,在当前文件大小达到某个阈值(或一层阈值)之前,您可以允许更长的时间。
另一个启发:如果文件大小自上次以来没有改变,表明当前文件上没有活动,则可以进一步将周期延长几分之一秒。(或者将文件的时间戳与当前时间进行比较?)。
在给定上下文的情况下,决定轮询周期的函数的具体参数通常取决于文件增长方式的细节。
- 文件增长到大小限制的10%的绝对最小时间是多少?
- 24小时内写入这些文件的平均数据量是多少?
- 白天和晚上不同时间的文件增长率有很大差异吗?
- 文件大小变化的频率是多少?
它是一种"连续的"类似于日志记录的活动,即平均每秒会发生几十次写操作,还是写操作发生的频率较低,但在写操作时一次会存储更多的数据? - 等。
或者,但是冒着在程序中引入操作系统特定逻辑的风险,您可以查看文件/目录更改通知系统。我知道WIN32 API和Linux都提供了各自的这类特性。我不知道操作系统使用的具体实现,这些实现可能会引入某种轮询方法,类似于您在外部使用的方法。然而,操作系统有各种到文件系统的钩子,这可能允许它实现一个更少的侵入性