You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Release模式下FileSystemWatcher无法同时监听两个目录事件问题

问题根本原因
  • Release模式下GC会执行更激进的可达性分析优化:如果一个对象在初始化之后,后续代码没有任何对它的读取/写入操作,GC就会判定该对象不再被使用,即使有变量持有它的引用,也会被标记为可回收。Debug模式下为了方便调试,会延长局部变量的生命周期到方法结束,所以不会出现回收问题。
  • 你代码里不管是最初的List<FolderWatch> lw还是后来的FileSystemWatcher[] w_arr,在完成初始化之后,后续Main方法只有Thread.Sleep(Timeout.InfiniteTimeSpan),没有任何对这两个变量的访问操作,所以Release模式下GC会将整个列表/数组和里面的FileSystemWatcher实例全部回收,自然就收不到后续的文件事件。
  • 你在SetupWatcher里加的GC.KeepAlive(watcher)完全无效:GC.KeepAlive的作用仅局限于当前方法的执行流程,方法执行完成返回后,这个保护就失效了,没办法阻止方法外的GC回收操作。
  • 至于硬编码能正常运行的情况,属于特定编译器版本下的优化差异,并不是稳定的可预期行为,本质上也存在被GC回收的风险。
解决方案

只要保证在Main方法的整个生命周期内,GC能判定FileSystemWatcher实例始终是可达的即可,可选方案如下:

方案1:在休眠后添加KeepAlive保护

在Thread.Sleep之后加一行对持有watcher的集合的KeepAlive调用,这行代码永远不会被执行到,但足以让GC判定该集合后续仍会被访问,不会提前回收:

Thread.Sleep(Timeout.InfiniteTimeSpan);
// 集合是w_arr就填w_arr,是List<FolderWatch>的lw就填lw
GC.KeepAlive(w_arr);

方案2:将持有watcher的集合提升为静态字段

静态字段的生命周期和应用程序域一致,GC永远不会回收静态字段引用的对象,是最稳妥的方案:

public class Program
{
    // 提升为类静态字段
    private static FileSystemWatcher[] _watchers;
    private static void Main(string[] args)
    {
        string[] paths = File.ReadAllLines("config.txt");
        _watchers = new FileSystemWatcher[paths.Length];
        for (int i = 0; i < paths.Length; i++)
        {
            if (Path.IsPathFullyQualified(paths[i]))
            {
                _watchers[i] = new FileSystemWatcher();
                SetupWatcher(_watchers[i], paths[i]);
            }
        }
        Thread.Sleep(Timeout.InfiniteTimeSpan);
    }
    // 其余方法保持不变
}

方案3:用循环替代无限休眠

如果后续有其他运行时逻辑,可以把Thread.Sleep换成循环,循环过程中访问watcher集合,也能阻止GC回收:

while (true)
{
    // 示例:定期打印监听状态,实际可替换为你的业务逻辑
    Console.WriteLine($"正在监听{_watchers.Count(x => x != null)}个目录");
    Thread.Sleep(TimeSpan.FromMinutes(1));
}

内容的提问来源于stack exchange,提问作者petr.f77

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 22:27:04