我想维护一个镜像特定目录的缓存,所以我添加一个监视,其事件由线程A监视,然后告诉线程B扫描该目录并将文件名放入我的缓存中。我有单独的线程,因为我希望应用程序在扫描期间仍然响应传入的inotify事件。否则,我可能会丢失事件,因为我没有读取它们并且在扫描期间填充了inotify队列。
完全有可能在文件的删除或move_from事件被目录扫描添加到我的缓存之前将被处理。在这种情况下,一个天真的实现最终会有一个缓存条目引用一个不存在的文件。什么是处理这种特殊竞争条件的正确方法?
答案 0 :(得分:0)
我做的方法是保留两个永久线程:一个实用程序线程和一个 inotify线程,用于从inotify文件描述符中不间断地读取。这些线程通过阻塞队列进行通信。
inotify thread 检测到事件时,可以是以下两种事件类型之一:
检测到后,事件立即排队到实用程序线程。
当实用程序线程收到第一类事件时,它会通过将完整目录内容读入缓存来从头开始重新创建整个缓存。当没有缓存时,同样的情况发生,并且第二类型的事件到来。在其他情况下,避免使用完整的readdir(),并根据事件简单地修改缓存。
只有在允许多个线程修改缓存时,才会出现问题中描述的竞争条件。所描述的方法通过假设允许修改缓存的唯一线程是实用程序线程来处理它。
如果要允许其他线程修改缓存(例如,因为您不知道文件系统是否支持inotify),您可以使用更简单,更健壮的方法:不跟踪单个目录修改事件和让实用程序线程对每个到达的事件执行完整的readdir()。在最坏的情况下会有太多的读取,但阅读目录内容本身是如此便宜,我不会在乎。
如果读取完整目录内容不便宜(例如,因为它可能非常非常大),那么您不应该将所有内容存储在内存中以开始。通过使用telldir
,seek
和fstat
来跟踪用户当前可见的少量文件,可以使用小部分缓存更快地刷新这种情况。