0

我正在将 filesystemwatcher 用于将在框架 1.1 上运行的程序,并且当发生更改时,它会发送 2 个信号说它已更改。通过研究,我知道这就是 Windows 的工作方式,我无法用 fsw 阻止这种情况。

但作为一种解决方法,我想让它接受第一个脉冲,然后锁定,所以另一个试图调用该方法的方法被忽略(或重定向?),因为我有一个备份系统,它是制作所有文件的 2 个副本,所以这是我真正需要解决的问题。代码的其他地方受到影响,我已经设法使用定时器来解决这个问题,方法是在调用定时器时立即阻止它,但是在这种情况下它会变得非常混乱,我相信必须有一个更清洁的解决方案。

代码:

private static void GetCurrentJob()
        { 
            ///Lots of code that isn't relevant 
        }
private static void ProgramSwapMonitor(string ProgramChange)
        {
            // Create a new FileSystemWatcher and set its properties.
            FileSystemWatcher watcher = new FileSystemWatcher
            {
                Path = ProgramChange,

                /* Watch for changes in LastAccess and LastWrite times, and 
                   the renaming of files or directories. */

                NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
               | NotifyFilters.FileName | NotifyFilters.DirectoryName,

                // Only watch this specific file
                Filter = "dnocontainer.cfg"
            };

            // Add event handlers.
            watcher.Changed += new FileSystemEventHandler(OnChanged);
            watcher.Created += new FileSystemEventHandler(OnChanged);
            watcher.Deleted += new FileSystemEventHandler(OnChanged);
            watcher.Renamed += new RenamedEventHandler(OnRenamed);

            // Begin watching.
            watcher.EnableRaisingEvents = true;
        }
        // Define the event handlers.
        private static void OnChanged(object source, FileSystemEventArgs e)
        {

            //This disables the directory monitor, then changes the active job in its memory, then restarts the directory monitor so it can now monitor the new location, then removes the old watcherChanged instance so theres no duplicates.
            fileSystemWatcher.EnableRaisingEvents = false;
            GetCurrentJob(); //This is the method that needs to be only running once, but is made to run twice
            MonitorDirectory(path);
            fileSystemWatcher.Changed -= new FileSystemEventHandler(FileSystemWatcher_Changed);
            
        }
4

2 回答 2

3

一个简单的方法是使用条件变量:

static private bool WatcherMutex;

static private void OnChanged(object source, FileSystemEventArgs e)
{
  if ( WatcherMutex ) return;
  WatcherMutex = true;
  try
  {
    ...
  }
  finally
  {
    WatcherMutex = false;
  }
}

另一种方法是从观察者中添加和删除处理程序以减轻事件池的进程,但这需要一个类字段而不是本地变量:

static private FileSystemWatcher Watcher = new FileSystemWatcher();
static private void SetWatcherHandlers(bool active)
{
  if ( active )
  {
    Watcher.Changed += OnChanged;
    Watcher.Created += OnChanged;
    Watcher.Deleted += OnChanged;
    Watcher.Renamed += OnRenamed;
  }
  else
  {
    Watcher.Changed -= OnChanged;
    Watcher.Created -= OnChanged;
    Watcher.Deleted -= OnChanged;
    Watcher.Renamed -= OnRenamed;
  }
}
static private void OnChanged(object source, FileSystemEventArgs e)
{
  SetWatcherHandlers(false);
  try
  {
    ...
  }
  finally
  {
    SetWatcherHandlers(true);
  }
}
static private void ProgramSwapMonitor(string ProgramChange)
{
  Watcher = new FileSystemWatcher
  {
    Path = ProgramChange,
    NotifyFilter = NotifyFilters.LastAccess 
                  | NotifyFilters.LastWrite
                  | NotifyFilters.FileName 
                  | NotifyFilters.DirectoryName,
    Filter = "dnocontainer.cfg"
  };
  SetWatcherHandlers(true);
  Watcher.EnableRaisingEvents = true;
}

或者您可以使用相同的类字段简单地启用和禁用观察者本身:

static private void OnChanged(object source, FileSystemEventArgs e)
{
  Watcher.EnableRaisingEvents = false;
  try
  {
    ...
  }
  finally
  {
    Watcher.EnableRaisingEvents = true;
  }
}

您可以使用更符合您需要的方式来启用和禁用整个观察EnableRaisingEvents程序,或者一次仅使用一个或多个处理程序,使用一些条件变量或SetWatcherHandlerXXXX一个或多个,或者直接添加/删除处理程序在处理程序方法中而不是条件变量中:

static private void OnChanged(object source, FileSystemEventArgs e)
{
  Watcher.Changed -= OnChanged;
  Watcher.Created -= OnChanged;
  Watcher.Deleted -= OnChanged;
  try
  {
    ...
  }
  finally
  {
    Watcher.Changed += OnChanged;
    Watcher.Created += OnChanged;
    Watcher.Deleted += OnChanged;
  }
}

因此,在这里我们将OnChanged行为OnRenamed视为不同。

于 2020-07-15T11:17:27.257 回答
0

您应该使用 Microsoft 的反应式框架(又名 Rx) - NuGetSystem.Reactive并添加using System.Reactive.Linq;- 然后您可以这样做:

IDisposable subscription =
    Observable
        .Merge(
            Observable.FromEventPattern<FileSystemEventHandler, FileSystemEventArgs>(h => watcher.Changed += h, h => watcher.Changed -= h),
            Observable.FromEventPattern<FileSystemEventHandler, FileSystemEventArgs>(h => watcher.Created += h, h => watcher.Created -= h),
            Observable.FromEventPattern<FileSystemEventHandler, FileSystemEventArgs>(h => watcher.Deleted += h, h => watcher.Deleted -= h))
        .GroupBy(x => x.EventArgs.FullPath)
        .Select(gxs => gxs.Throttle(TimeSpan.FromSeconds(0.25)))
        .Merge()
        .Subscribe(x => GetCurrentJob());

现在,那有什么作用?

  • 首先,它将所有三个事件(ChangedCreatedDeleted)合并为一个事件流。

  • 然后,它会执行一个GroupBy操作,FullName以便对任何文件的每个单独更改都被视为该文件具有自己的事件。

  • 然后它会限制每个事件,以便在 0.25 秒内发生FullName另一个事件时忽略任何事件。FullName

  • 然后它将所有FullName事件合并为一个事件流。

  • 最后,它一次调用GetCurrentJob一个。

这消除了您对暂停事件等的许多担忧。

如果您想停止事件,只需调用subscription.Dispose().

于 2020-07-16T11:25:28.473 回答