c#/c++ 通过系统api监视文件变化的问题

news/2024/4/29 5:04:41/文章来源:https://blog.csdn.net/nightwizard2030/article/details/131532867

再分享个比较经典的案例,在很多场景下,我们都要去监视某个文件夹下的文件变化,在创建、修改或删除的时候触发一些行为。众所周知,c#有个实现类叫FileSystemWatcher,可以用来监视目录包括子目录下文件的变化,这样就不需要不断的循环去递归扫目录,节省很大的资源开销,而且响应速度也更快。从本质上来说,无论在win还是linux上,都是通过封装系统api进行实现的,所以这个坑,其实是并非是.net封装的问题,而是一个无法绕过的问题。

先来看一个示例demo

using System;
using System.IO;class Program
{static void Main(){// 要监视的目录路径string pathToWatch = @"C:\Path\To\Your\Directory";// 创建一个新的 FileSystemWatcher 实例using (FileSystemWatcher watcher = new FileSystemWatcher()){// 设置要监视的目录watcher.Path = pathToWatch;// 设置要监视的文件和目录的更改类型watcher.NotifyFilter = NotifyFilters.LastWrite| NotifyFilters.FileName| NotifyFilters.DirectoryName;//包含子目录监视watcher.IncludeSubdirectories = true;// 启用事件引发watcher.EnableRaisingEvents = true;// 添加事件处理程序watcher.Created += OnCreated;watcher.Deleted += OnDeleted;watcher.Changed += OnChanged;watcher.Renamed += OnRenamed;// 监视状态Console.WriteLine($"正在监视目录:{pathToWatch}");Console.WriteLine("按任意键退出.");Console.ReadKey();}}// 文件或目录创建事件处理程序private static void OnCreated(object sender, FileSystemEventArgs e){Console.WriteLine($"新文件或目录已创建: {e.Name} - 类型: {e.ChangeType}");}// 文件或目录删除事件处理程序private static void OnDeleted(object sender, FileSystemEventArgs e){Console.WriteLine($"文件或目录已删除: {e.Name} - 类型: {e.ChangeType}");}// 文件或目录更改事件处理程序private static void OnChanged(object sender, FileSystemEventArgs e){Console.WriteLine($"文件或目录已更改: {e.Name} - 类型: {e.ChangeType}");}// 文件或目录重命名事件处理程序private static void OnRenamed(object sender, RenamedEventArgs e){Console.WriteLine($"文件或目录已重命名: {e.OldName} -> {e.Name} - 类型: {e.ChangeType}");}
}

这段示例看起来没有任何问题,但实际使用的时候会发现,有些连续创建的文件,根本扫不到。

测试环境.net6 linux,比如监视AAA文件夹,然后用程序创建AAA\BBB和AAA\BBB\123.txt,会发现能监听到BBB的创建,但却没有AAA\BBB\123.txt的通知。

再来看下windows c++上的demo

#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <tchar.h>void RefreshDirectory(LPTSTR);
void RefreshTree(LPTSTR);
void WatchDirectory(LPTSTR);void _tmain(int argc, TCHAR *argv[])
{if(argc != 2){_tprintf(TEXT("Usage: %s <dir>\n"), argv[0]);return;}WatchDirectory(argv[1]);
}void WatchDirectory(LPTSTR lpDir)
{DWORD dwWaitStatus; HANDLE dwChangeHandles[2]; TCHAR lpDrive[4];TCHAR lpFile[_MAX_FNAME];TCHAR lpExt[_MAX_EXT];_tsplitpath_s(lpDir, lpDrive, 4, NULL, 0, lpFile, _MAX_FNAME, lpExt, _MAX_EXT);lpDrive[2] = (TCHAR)'\\';lpDrive[3] = (TCHAR)'\0';// Watch the directory for file creation and deletion. dwChangeHandles[0] = FindFirstChangeNotification( lpDir,                         // directory to watch FALSE,                         // do not watch subtree FILE_NOTIFY_CHANGE_FILE_NAME); // watch file name changes if (dwChangeHandles[0] == INVALID_HANDLE_VALUE) {printf("\n ERROR: FindFirstChangeNotification function failed.\n");ExitProcess(GetLastError()); }// Watch the subtree for directory creation and deletion. dwChangeHandles[1] = FindFirstChangeNotification( lpDrive,                       // directory to watch TRUE,                          // watch the subtree FILE_NOTIFY_CHANGE_DIR_NAME);  // watch dir name changes if (dwChangeHandles[1] == INVALID_HANDLE_VALUE) {printf("\n ERROR: FindFirstChangeNotification function failed.\n");ExitProcess(GetLastError()); }// Make a final validation check on our handles.if ((dwChangeHandles[0] == NULL) || (dwChangeHandles[1] == NULL)){printf("\n ERROR: Unexpected NULL from FindFirstChangeNotification.\n");ExitProcess(GetLastError()); }// Change notification is set. Now wait on both notification 
// handles and refresh accordingly. while (TRUE) { // Wait for notification.printf("\nWaiting for notification...\n");dwWaitStatus = WaitForMultipleObjects(2, dwChangeHandles, FALSE, INFINITE); switch (dwWaitStatus) { case WAIT_OBJECT_0: // A file was created, renamed, or deleted in the directory.// Refresh this directory and restart the notification.RefreshDirectory(lpDir); if ( FindNextChangeNotification(dwChangeHandles[0]) == FALSE ){printf("\n ERROR: FindNextChangeNotification function failed.\n");ExitProcess(GetLastError()); }break; case WAIT_OBJECT_0 + 1: // A directory was created, renamed, or deleted.// Refresh the tree and restart the notification.RefreshTree(lpDrive); if (FindNextChangeNotification(dwChangeHandles[1]) == FALSE ){printf("\n ERROR: FindNextChangeNotification function failed.\n");ExitProcess(GetLastError()); }break; case WAIT_TIMEOUT:// A timeout occurred, this would happen if some value other // than INFINITE is used in the Wait call and no changes occur.// In a single-threaded environment you might not want an// INFINITE wait.printf("\nNo changes in the timeout period.\n");break;default: printf("\n ERROR: Unhandled dwWaitStatus.\n");ExitProcess(GetLastError());break;}}
}void RefreshDirectory(LPTSTR lpDir)
{// This is where you might place code to refresh your// directory listing, but not the subtree because it// would not be necessary._tprintf(TEXT("Directory (%s) changed.\n"), lpDir);
}void RefreshTree(LPTSTR lpDrive)
{// This is where you might place code to refresh your// directory listing, including the subtree._tprintf(TEXT("Directory tree (%s) changed.\n"), lpDrive);
}

可以看到,这段代码里面,有个问题,就是文件夹中如果文件创建在RefreshTree之后,FindNextChangeNotification之前,则会漏掉。所以在dotnet上,实际上并没有使用这种方式,而是通过ReadDirectoryChangesW 去实现的,这种基于buffer的,理论不溢出,就不会出现丢失的情况。所以在windows下,只要buffer足够大,fsw是不会漏掉任何一个文件的。

 那么来到linux,从源码从可以看到是基于inotify的,读下源码第一句话,就真相大白了

所以在linux下,我的那种场景,刚好就触发了这个问题,这种是基于inotify的缺陷,因为这玩意也没buffer,我猜测与上面c++的demo出现的问题类似。

总结一下,使用fsw千万需要小心,在win和linux上的表现是不同的,win上可以放心用,linux上可能会漏文件,需要在自己场景下特定的时间点进行检测。不然可能会触发意想不到的BUG

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_326400.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Pygame中获取键盘按键的方法

1 事件与队列 在Pygame中&#xff0c;将用户对游戏的操作叫做“事件”。键盘按键是一种事件&#xff0c;鼠标点击和游戏手柄的输入也是一种事件。在Pygame的子模块locals中&#xff0c;对这些事件进行了定义。当用户通过键盘、鼠标或者游戏手柄对游戏进行操作后&#xff0c;产…

Stable Diffusion 模型界面介绍

Stable Diffusion 模型界面介绍 界面1 图1 Stable Diffusion 模型界面1 ①&#xff1a;选择的模型&#xff0c;及Stable Diffusion进行生成图片是使用的模型。其中.ckpt为大模型 ②&#xff1a;prompt --> 正向提示词。表示你的想法&#xff0c;你想要生成一副什么样的图…

IMX6ULL 移植篇-uboot 网络命令NFS

一. uboot 网络操作命令 本文介绍 nfs 命令的使用&#xff0c;具体是&#xff1a;通过 NFS服务向开发板下载 zImage内核镜像文件。 二. nfs 命令 nfs命令使用的目的&#xff1a;为了方便开发板调试。 nfs(Network File System) 网络文件系统&#xff0c;通过 nfs 可以在计算…

Redis【实战篇】---- 达人探店

Redis【实战篇】---- 达人探店 1. 达人探店 - 发布探店笔记2. 达人探店 - 查看探店笔记3. 达人探店 - 点赞功能4. 达人探店 - 点赞排行榜 1. 达人探店 - 发布探店笔记 发布探店笔记 探店笔记类似点评网站的评价&#xff0c;往往是图文结合。对应的表有两个&#xff1a; tb_bl…

记录力扣热题-100——从链表中找到刷题感觉

目录 一. &#x1f981; 前言二. &#x1f981; 解题过程1. 题目2. 思路一3. 思路二 三. &#x1f981; 文末活动内容简介本书结构关于代码 一. &#x1f981; 前言 狮子此前已经很久没有碰过算法题了&#xff0c;对于之前好不容易攒起来的题感又没了…最近准备面试&#xff0…

这些代码,差点把我气出内伤

先问大家一个小问题&#xff1a;你觉得看别人代码累&#xff0c;还是自己写代码累&#xff1f; 我相信有很多朋友会说&#xff0c;当然是自己写代码累了&#xff0c;要思考逻辑、要动手敲键盘&#xff0c;身心俱疲啊&#xff1b;但是&#xff0c;如果你需要经常阅读别人的代码…

leetcode 141.环形链表(快慢指针追击问题)

⭐️ 往期相关文章 &#x1f4ab;链接1&#xff1a;链表分割 &#x1f4ab;链接2&#xff1a;链表中倒数第k个结点(快慢指针问题) &#x1f4ab;链接3&#xff1a;leetcode 876.链表的中间结点(快慢指针问题) &#x1f4ab;链接4&#xff1a;leetcode 206.反转链表 &#x1f4…

vue echarts k线图 子功能设置

1 图中自定义选择区间, 手动鼠标拉取区间显示 2 底部数据选择条 dataZoom: [{type: inside,xAxisIndex: [0, 1],start: 98,end: 100},{show: true, // 这个是打开数据 选择条xAxisIndex: [0, 1],type: slider,top: 85%,start: 98,end: 100}], 3 鼠标在 k线图 选择区域 显示 的…

简历石沉大海!这份新鲜出炉的测试用人需求分析报告揭示了原因

最近有朋友吐槽简历投递后石沉大海&#xff0c;而主动打电话面试的除了外包还是外包。软件测试就业形势真的这么糟糕了&#xff1f; 小酋决定用数据揭开真相。因此小酋选取“软件测试”、“自动化测试”、“测试开发”作为搜索关键词&#xff0c;统计了 无忧网 近一个月用人市…

118.浏览器支持和修复Safari浏览器的Flexbox漏洞

在我们之前的文章中&#xff0c;我们介绍了测试的步骤 虽然现在大部分新版本的浏览器都能支持99%的CSS属性&#xff0c;但是不排除的是仍然有一些用户使用老的IE浏览器或者版本较低的浏览器去浏览我们的网页&#xff0c;这样我们的网站可能无法按照我们的预期工作&#xff1b…

[HNOI2008] 越狱

1.介绍 原题链接(回去补上,教练把锣鼓禁了qwq谴责这种行为&#xff01;&#xff01;&#xff01;) 一句话题意&#xff1a;房间1到房间n中&#xff0c;求存在相邻两个房间的宗教相同的可能性总和 就这&#xff1f;省选&#xff1f;哪个省的&#xff1f;湖南的&#xff1f;我…

学习小波分析的一些资料

Papers Wavelets and Subband Coding (2007) - M. Vetterli, J. Kovačević Tutorials A Really Friendly Guide to Wavelets (1999) - C. Valens [CiteSeerX][Mirror]A Practical Guide to Wavelet Analysis (1998) - C. Torrence , G. P. Compo [CiteSeerX]Basics of Wav…

hadoop --- MapReduce

MapReduce定义&#xff1a; MapReduce可以分解为Map (映射) Reduce (规约) &#xff0c; 具体过程&#xff1a; Map : 输入数据集被切分成多个小块&#xff0c;并分配给不同的计算节点进行处理Shuffle and Sort&#xff1a;洗牌和排序&#xff0c;在 Map 阶段结束后&#xf…

日本 NFT 项目概览与特点总结

日本的 NFT 市场 日本的 NFT 市场起源于与国内动漫和娱乐偶像的合作&#xff0c;重点关注本土文化&#xff0c;文化成为日本 NFT 项目的重要基石。 关键要点&#xff1a; 日本的 NFT 产业具有三个特点&#xff1a;广泛的知识产权&#xff08;IP&#xff09;、低 FUD 水平以及…

看完就会,从抓包到接口测试的全过程解析

一、为什么抓包 从功能测试角度 通过抓包查看隐藏字段 Web 表单中会有很多隐藏的字段&#xff0c;这些隐藏字段一般都有一些特殊的用途&#xff0c;比如收集用户的数据&#xff0c;预防 CRSF 攻击&#xff0c;防网络爬虫&#xff0c;以及一些其他用途。这些隐藏字段在界面上…

在idea中使用Git技术

1.配置git环境 打开idea,点击file->setting->搜索git&#xff0c; 将git的安装路径填写进去 2.去gitee创建一个远程仓库 3.拉入一个.gitignore文件&#xff0c;过滤掉不需要管理的文件 4.在idea进行如下操作 5.选择要提交的内容 目前只是保存在了本地仓库 6.推送到远端…

28-大文件上传(了解)

一、是什么&#xff1f; &#x1f697;&#x1f697;&#x1f697;不管怎样简单的需求&#xff0c;在量级达到一定层次时&#xff0c;都会变得异常复杂。 文件上传简单&#xff0c;文件变大就复杂 上传时&#xff0c;以下几个注意点会影响用户体验 服务器处理数据的能力请求超…

Mycat2 使用教程(三)原始数据导入分库分表【MySQL分库分库分表】

Mycat2 使用教程&#xff08;三&#xff09;原始数据导入分库分表【MySQL分库分库分表】 本文主要描述mycat2完成分库分别数据源配置后&#xff0c;将数据导入的过程mysql 分库分表如果是新项目&#xff0c;则不用考虑本文内容mycat2如何配置分库分表&#xff1f;见上文 1.计…

5个网站帮你找到免费优质的视频素材

5个免费可商用视频素材网站&#xff0c;建议收藏&#xff01; 潮点视频 https://shipin520.com/shipin-mb/all-def-267-all-all-all-all-all-0-1.html?from_code2510 潮点视频是一个提供优质高清、无水印的视频素材网站&#xff0c;站内有大量的AE模板、PR模板、实拍素材、视…

自定义 MVC 框架思想

目录 一、MVC设计模式 1. 什么是MVC 2. 三层架构与MVC的区别 二、自定义MVC框架 1. 为什么要学习自定义MVC框架 2. 自定义MVC的工作原理 3. 自定义MVC框架的优势 三、自定义MVC实例流程 1. mvc三层架构的弊端 2. 自定义MVC的工作流程 2.1 子控制器&#xff08;…