Python:以友好的方式等待文件达到大小限制

2 投票
4 回答
1209 浏览
提问于 2025-04-16 21:37

我正在用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 个回答

1

你说得对,轮询(polling)通常不是最好的解决方案,相比其他方法,它效率较低。不过,有时候它是最简单的办法,特别是当你想写一个可以在Windows和Linux/UNIX上都能运行的程序时。

幸运的是,现在的硬件速度很快。在我的电脑上,我可以让你的循环每秒轮询十次,几乎看不出对CPU使用率的影响。在Windows任务管理器中,CPU使用率几乎没有变化。每秒轮询一百次虽然会在使用率图上出现一些小波动,但CPU使用率偶尔也只会达到1%。

像你例子中提到的,每十秒轮询一次,对CPU使用率来说几乎可以忽略不计。

如果你觉得重要的话,还可以给你的脚本设置一个较低的优先级,这样就不会影响到电脑上其他任务的性能。

2

你说得对:“轮询是个坏主意”。如果你频繁地去检查某个事情是否发生,CPU就会浪费很多资源,尤其是当什么都没发生的时候。如果你检查得少一些,虽然能省点资源,但当事情真的发生时,你就会延迟处理。

不过,唯一的替代方法就是“阻塞”,也就是等到你收到某种“信号”再继续。

如果你在使用Linux系统,可以试试“inotify”:

http://linux.die.net/man/7/inotify

4

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 的解决方案。

这里有一篇不错的文章,讲的是 跨平台文件系统监控

撰写回答