跳过fsevents队列中的步骤

时间:2014-09-23 05:43:06

标签: python callback fsevents python-watchdog

我正在使用fsevents监控文件夹。每次添加文件时,都会在此文件上执行代码。每秒都会在文件夹中添加一个新文件。

from fsevents import Observer, Stream

def file_event_callback(event):
    # code 256 for adding file to folder
    if event.mask == 256:
        fileChanged = event.name
        # do stuff with fileChanged file

if __name__ == "__main__":
    observer = Observer()   
    observer.start()
    stream = Stream(file_event_callback, 'folder', file_events=True)
    observer.schedule(stream)
    observer.join()

这很有效。唯一的问题是,libary正在为添加到该文件夹​​的每个文件构建队列。在file_event_callback中执行的代码可能需要超过一秒钟。当发生这种情况时,应该跳过队列中的其他项目,以便仅使用最新的项目。

如何从队列中跳过项目,以便只完成最后一个项目后使用的文件夹的最新添加?

我首先尝试使用看门狗,但由于这必须在Mac上运行,我遇到了一些问题,使其按照我想要的方式运行。

1 个答案:

答案 0 :(得分:0)

我不确切知道你正在使用什么库,当你说“这是在建一个队列......”我不知道你指的是什么“这个”......但一个明显的答案是坚持你的在其正在使用的任何内容前面拥有queue,因此您可以直接操作该队列。例如:

import queue
import threading

def skip_get(q):
    value = q.get(block=True)
    try:
        while True:
            value = q.get(block=False)
    except queue.Empty:
        return value

q = queue.Queue()

def file_event_callback(event):
    # code 256 for adding file to folder
    if event.mask == 256:
        fileChanged = event.name
        q.put(fileChanged)

def consumer():
    while True:
        fileChanged = skip_get(q)
        if fileChanged is None:
            return
        # do stuff with fileChanged

现在,在启动观察者之前,请执行以下操作:

t = threading.Thread(target=consumer)
t.start()

最后:

observer.join()
q.put(None)
t.join()

那么,这是如何工作的?

首先,让我们看一下消费者方面。当你拨打q.get()时,会弹出队列中的第一件事。但如果什么都没有呢?这就是block参数的用途。如果它为假,get将引发queue.Empty异常。如果是真的,get将永远等待(以线程安全的方式),直到出现某些东西。因此,通过阻止一次,我们处理的是没有什么可读的情况。然后通过循环而不阻塞,我们消耗队列中的任何其他东西,以处理有太多东西要读取的情况。因为我们不断地将value重新分配给我们弹出的任何内容,我们最终得到的是队列中的最后一件事。

现在,让我们来看看制作人。当您致电q.put(value)时,只需将value放入队列即可。除非你对队列设置了一个大小限制(我没有),否则它无法阻止,所以你不必担心这些。但是现在,您如何向消费者线索发出信号?它将永远等待q.get(block=True);唤醒它的唯一方法是给它一些流行的价值。通过推送一个sentinel值(在这种情况下,None很好,因为它无效作为文件名),并通过退出让消费者处理None,我们给自己一个很好的,干净的方式关掉。 (因为我们从未在None之后推送任何内容,因此不会意外地跳过它。)因此,我们可以推送None,然后确保(禁止任何其他错误)消费者线程将最终退出,这意味着我们可以t.join()等待,直到它不用担心死锁。


我在上面提到过,你可以使用Condition更简单地做到这一点。如果你考虑一个队列实际上是如何工作的,它只是一个受条件保护的列表(或deque,或其他):消费者等待条件直到有可用的东西,并且生产者通过将其添加到列表中来提供可用的东西。发出信号。如果你只想要最后一个值,那么列表就没有理由了。所以,你可以这样做:

class OneQueue(object):
    def __init__(self):
        self.value = None
        self.condition = threading.Condition()
        self.sentinel = object()
    def get(self):
        with self.condition:
            while self.value is None:
                self.condition.wait()
            value, self.value = self.value, None
            return value
    def put(self, value):
        with self.condition:
            self.value = value
            self.condition.notify()
    def close(self):
        self.put(self.sentinel)

(因为我现在正在使用None来表示没有任何东西可用,我必须创建一个单独的标记来表明我们已经完成了。)

这种设计的问题在于,如果生产者在消费者忙于处理它们时放置多个值,它可能会错过其中的一些 - 但在这种情况下,“问题”正是您所寻找的。

尽管如此,使用较低级别的工具总是意味着还有更多错误,这对线程同步尤其危险,因为它涉及难以解决的问题,即使您理解也难以调试他们,所以你最好还是使用Queue