Python:以友好的方式等待文件达到大小限制
我正在用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)太长,当文件快速增长时,我就无法准确知道文件的大小;而如果我设置的检查间隔太短,又会浪费计算机的处理能力。
谢谢!
我该怎么做呢?
谢谢!
4 个回答
你说得对,轮询(polling)通常不是最好的解决方案,相比其他方法,它效率较低。不过,有时候它是最简单的办法,特别是当你想写一个可以在Windows和Linux/UNIX上都能运行的程序时。
幸运的是,现在的硬件速度很快。在我的电脑上,我可以让你的循环每秒轮询十次,几乎看不出对CPU使用率的影响。在Windows任务管理器中,CPU使用率几乎没有变化。每秒轮询一百次虽然会在使用率图上出现一些小波动,但CPU使用率偶尔也只会达到1%。
像你例子中提到的,每十秒轮询一次,对CPU使用率来说几乎可以忽略不计。
如果你觉得重要的话,还可以给你的脚本设置一个较低的优先级,这样就不会影响到电脑上其他任务的性能。
你说得对:“轮询是个坏主意”。如果你频繁地去检查某个事情是否发生,CPU就会浪费很多资源,尤其是当什么都没发生的时候。如果你检查得少一些,虽然能省点资源,但当事情真的发生时,你就会延迟处理。
不过,唯一的替代方法就是“阻塞”,也就是等到你收到某种“信号”再继续。
如果你在使用Linux系统,可以试试“inotify”:
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)
Windows 解决方案
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)
OSX 解决方案
在 OSX 文件系统中也有类似的功能,可以使用 PyKQueue。如果你能理解这些例子,可以自己搜索一下 OSX 的解决方案。
这里有一篇不错的文章,讲的是 跨平台文件系统监控。