我正在使用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上运行,我遇到了一些问题,使其按照我想要的方式运行。
答案 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
。