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

news2024/11/26 12:29:06

再分享个比较经典的案例,在很多场景下,我们都要去监视某个文件夹下的文件变化,在创建、修改或删除的时候触发一些行为。众所周知,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.coloradmin.cn/o/718582.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

亚马逊云科技,定义了生成式AI的生产力

众所周知&#xff0c;要把大模型转化为生产力&#xff0c;AI模型、算力和数据会是难以逾越的门槛。新的目标已经出现&#xff0c;我们是否有了足够强大的AI基础设施&#xff1f;在6月28日上海举行的峰会上&#xff0c;亚马逊云科技展示了这样的能力。 从掀起AI画图热潮的Stabil…

【深度学习笔记】深层神经网络

本专栏是网易云课堂人工智能课程《神经网络与深度学习》的学习笔记&#xff0c;视频由网易云课堂与 deeplearning.ai 联合出品&#xff0c;主讲人是吴恩达 Andrew Ng 教授。感兴趣的网友可以观看网易云课堂的视频进行深入学习&#xff0c;视频的链接如下&#xff1a; 神经网络和…

select 框添加树结构(todu)

1. 案例: 2. 代码 下班了&#xff0c;明天写

蓝桥杯专题-真题版含答案-【马虎的算式】【振兴中华】【黄金连分数】【打印十字图】

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列点击跳转>蓝桥系列 &#x1f449;关于作者 专注于Android/Unity和各种游…

day27-MySQL拓展

0目录 第六章 MySQL拓展 1.MySQL拓展 1.1 事务定义&#xff1a;事务就是将一组SQL语句放在同一批次内去执行&#xff0c;如果一个SQL语句出错&#xff0c;则该批次内的所有sql都将被取消执行。 1.2 事务ACID原则 (1)原子性 一个事务要么全部提交成功&#xff0c;要么全部…

「深度学习之优化算法」(九)群搜索算法

1. 群搜索算法简介 (以下描述,均不是学术用语,仅供大家快乐的阅读)   群搜索算法(Group Search Optimizer)是一种基于发现者,跟随者,游荡者模型而产生的算法。算法模型较为复杂,提出时间也不长,对于其的深度研究相对较少,但也有一定的应用研究。   在群搜索算法…

【雕爷学编程】Arduino动手做(150)---旋转角度传感器模块

37款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&am…

【IDE 小程序】小程序控制台 不打印 console.log问题

小程序控制台 不打印 console.log问题 全局搜索compress.drop_console&#xff08;一般在config文件中&#xff09;&#xff0c;设置为false&#xff0c;再重新打开小程序即可

4、架构:Canvas VS DOM

在可视化搭建的低代码平台中&#xff0c;设计器是一个非常关键的模块&#xff0c;可以帮助用户通过拖拽、配置等方式快速搭建应用界面。 在技术选型方面&#xff0c;目前市面上主流的设计器技术包括基于 HTML/CSS/JavaScript 的 Web 设计器。 在渲染方案方面&#xff0c;主流…

TiDB(6):数据迁移-TiDB Lightning

1 TiDB Lightning介绍 TiDB Lightning 是一个将全量数据高速导入到 TiDB 集群的工具&#xff0c;目前支持 Mydumper 或 CSV 输出格式的数据源。你可以在以下两种场景下使用 Lightning&#xff1a; 迅速导入大量新数据。 备份恢复所有数据。 TiDB Lightning 主要包含两个部分…

NSS [SWPUCTF 2021 新生赛]jicao

NSS [SWPUCTF 2021 新生赛]jicao 先看题目&#xff0c;要求我们post传参一个id。然后get一个变量叫json&#xff0c;并且通过json_decode()对一串JSON进行解码 payload&#xff1a; GET&#xff1a; ?json{"x":"wllm"} POST&#xff1a;idwllmNB

java中处理异常规范

java处理异常规范 文章目录 java处理异常规范一、尽量不要使用e.printStackTrace(),而是使用log打印,并记录exception二、不要用一个Exception捕捉所有可能的异常三、使用finally关闭流资源或者直接使用try-with-resource四、捕获异常与抛出异常必须是完全匹配&#xff0c;或者…

Spring Boot 中的 Redis 分布式锁

Spring Boot 中的 Redis 分布式锁 在分布式系统中&#xff0c;多个进程同时访问共享资源时&#xff0c;很容易出现并发问题。为了避免这些问题&#xff0c;我们可以使用分布式锁来保证共享资源的独占性。Redis 是一款非常流行的分布式缓存&#xff0c;它也提供了分布式锁的功能…

react - TS

TypeScript TypeScript 简称『TS』&#xff0c;是微软开发的一个开源的编程语言。 一、TS 特点 TS 主要有如下几个特点: 完全兼容 JavaScript&#xff0c;是 JavaScript 的超集 引入类型系统&#xff0c;可以尽早的定位错误位置, 帮助提升开发效率 let obj {a:1,b:2....}o…

四氯化硅铝的活性离子蚀刻

引言 铝膜广泛用于集成电路中的互连线。随着电路的集成规模和密度变得越来越大&#xff0c;有必要将互连线和间隔的宽度减小到几乎薄膜厚度。为了实现这一点&#xff0c;已经开发了具有定向蚀刻能力的平行平面电极型等离子体蚀刻。这被称为反应离子蚀刻&#xff0c;因为它采用…

基于免疫优化算法的物流配送中心选址规划研究(Matlab实现)

目录 1 概述 2 物流配送中心选址规划研究 3 Matlab代码 4 结果 1 概述 影响物流配送中心选址的因素有很多,精确选址优化问题亟待解决。通过充分考虑货物的配送时间,将免疫算法加入其中,介绍了物流配送选址模型的构建以及免疫算法实现的相关步骤,最后利用matlab软件进行分析,提出…

手机端三维人体建模软件-易模,为虚拟现实、游戏开发带来新体验

三维人体建模是一种高级的数字技术&#xff0c;用于将人体的形态、肌肉、骨骼等三维信息转化为数字模型。这项技术涵盖了计算机图像处理、计算机辅助设计等领域&#xff0c;具有广泛的应用价值。通过三维人体建模&#xff0c;可以更好地理解人体的结构和功能&#xff0c;对于医…

弗迪科技携手纷享销客共建CRM系统,数智化升级加速“灯塔工厂”征程

当前&#xff0c;全球新一轮科技革命正和产业升级融合发展&#xff0c;数字化技术成为各行各业升级发展的重要推动力。 自2018年起&#xff0c;世界经济论坛与麦肯锡咨询公司发起“灯塔工厂”项目&#xff0c;全球严选制造业数字化转型典范作为“数字化制造”和“全球化4.0”的…

squid代理服务应用

squid代理服务器 代理的工作机制&#xff1a; 代理服务器的概念及其作用&#xff1a; 其主要作用有&#xff1a; Squid 代理的类型&#xff1a; 安装 Squid 服务 编译安装 Squid 修改 Squid 的配置文件 Squid 的运行控制 创建 Squid 服务脚本 构建传统代理服务器 生产环境…

红黑树:变色旋转规则化抽象逻辑分析

文章目录 一.红黑树的定义红黑树平衡性论证 二.红黑树的节点插入插入新节点后最小违规子结构(抽象分析)最小违规子结构一号的规则化算法分析最小违规子结构二号的规则化算法分析 三.红黑树类代码托管四.红黑树与AVL树的对比 旷世奇才发明的数据结构 一.红黑树的定义 红黑树的节…