【发布时间】:2018-04-17 16:19:47
【问题描述】:
我想维护一个反映特定目录的缓存,所以我添加了一个监视器,其事件由线程 A 监视,然后告诉线程 B 扫描该目录并将文件名放入我的缓存中。我有单独的线程,因为我希望应用程序在扫描期间仍然响应传入的 inotify 事件。否则,我可能会丢失事件,因为我没有读取它们并且在扫描期间 inotify 队列已满。
完全有可能在目录扫描将文件添加到我的缓存之前处理文件的 delete 或 move_from 事件。在这种情况下,一个简单的实现最终会拥有一个引用不存在的文件的缓存条目。处理这种特殊竞争条件的正确方法是什么?
【问题讨论】:
-
事情实际上比你想象的还要糟糕:目录可以在 在 readdir 调用期间发生变化,从而导致结果中的文件条目丢失或重复。但这在实践中并不重要——只需将
File#list的结果视为文件列表,可能 在那里并充分处理任何FileNotFoundExceptions。更重要的是-从您的问题看来,您正在尝试从两个线程中修改缓存。不要那样做。让您的 inotify 循环线程从 inotify 描述符中读取;当偶数出现时,向阅读器线程发送通知,以便它为您修改缓存。 -
我不确定你在说什么,为什么 inotify 阅读器线程会向临时目录阅读器线程发送一些东西?即使只有一个线程实际管理缓存,您仍然会遇到排序问题。如果管理缓存的任何线程接收到 delete 或 move_from 事件(删除不存在的缓存条目),然后是添加到缓存事件(来自临时 readdir 线程),添加到缓存事件会创建一个引用文件的缓存条目已经被删除了。解决方案是什么,在删除时创建一个缓存条目并在添加时删除它?逻辑不通。
-
"如果管理缓存的任何线程接收到 delete 或 move_from 事件(删除不存在的缓存条目),然后是添加到缓存事件(来自临时 readdir 线程)" - 你为什么突然引入第三个线程?您的问题描述了从描述符读取的单个线程和执行 readdir 的另一个线程......没有理由将更多线程引入方程式。即使是第二个线程也大多是不必要的。 “我不确定……为什么 inotify 阅读器线程会发送一些东西”——在尝试进行更多的多线程编程之前,请阅读有关消息队列的信息
-
我没有添加第三个线程。有一个 inotify 线程和一个 readdir 线程。我想象后者是暂时的,将“找到文件”事件发送给另一个,然后退出。无论谁发送给谁或使用何种通信机制,都需要解决竞赛。如果我确定在使用 readdir 处理目录时 inotify 队列无法填满并开始丢弃事件,我就不会再为第二个线程而烦恼了。
-
啊,我明白了。发送多个“找到文件”事件会相当昂贵,所以我没有想到这种情况。相反,我想,您的 readdir 线程只是直接清除特定目录的缓存并从 readdir() 结果重新填充它(如果在此期间有新事件到达,inotify 线程会取消预定的 readdir())。