我正在尝试使用 .Net 8 创建一个 Windows 服务应用程序作为托管服务,我的应用程序的想法是监视文件夹列表。问题是我正在丢失由
Created
触发的事件 FileSystemWatcher
。在上下文中,我的应用程序应该以这种方式工作:
这是我的快乐之路,当我调试某些文件时效果很好,但如果文件上传非常重,那么应用程序会丢失
FileSystemWatcher
的事件。
我正在尝试用生产者/消费者模式解决这个问题。我关注这些文章:
但对我不起作用。
我的代码看起来:
// Worker.cs
public class Worker : BackgroundService
{
private readonly ConcurrentQueue<FileTask> _fileTasks = new ConcurrentQueue<FileTask>();
private readonly SemaphoreSlim _signal = new SemaphoreSlim(0);
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
try
{
_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
_logger.LogInformation("Starting sync files.");
_synchronizer.SetUp();
while (!stoppingToken.IsCancellationRequested)
{
await _signal.WaitAsync(stoppingToken);
if (_fileTasks.TryDequeue(out FileTask? task) && task is not null && task.Process is not null && task.FileRelation is not null)
{
await task.Process(task.FileRelation, stoppingToken);
}
await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
}
}
catch (Exception exception)
{
_logger.LogError($"An unhandled error has occurred. Exception {exception}");
throw;
}
}
}
// FileServiceSynchronizer.cs
public class FileServiceSynchronizer : ISynchronizer
{
public void SetUp()
{
Task.Factory.StartNew(() =>
{
_logger.LogInformation($"[Watching] {location.FileSystemLocation}");
CustomFileSystemWatcher watcher = new CustomFileSystemWatcher(location.FileSystemLocation)
{
IncludeSubdirectories = true,
EnableRaisingEvents = true,
Filter = "",
CloudLocation = location.CloudLocation,
FileSystemErrorLocation = location.FileSystemErrorLocation,
NotifyFilter = NotifyFilters.DirectoryName
| NotifyFilters.FileName
| NotifyFilters.LastAccess
| NotifyFilters.LastWrite
};
watcher.Created += OnFileCreated;
});
}
//_fileRead.Process: Validate and Upload to Bucket.
private void OnFileCreated(object sender, FileSystemEventArgs e)
{
worker.EnqueuedFileTask(new FileTask()
{
FileRelation = new RelationLocation()
{
FileSystemLocation = e.FullPath,
CloudLocation = request.CloudLocation,
FileSystemErrorLocation = request.FileSystemErrorLocation
},
Process = _fileRead.Process
});
}
}
实际上,我正在尝试使用 Sqlite 解决此问题,其中我将事件(文件路径)发送到数据库(而不是排队文件任务),然后在 Worker 类中尝试获取路径和进程。但我仍然会输掉一些事件,直到什么都没有了。
更新
为了最终一切正常,只需将我的
FileSystemWatcher
更改为FileWatcherEx并保留此处使用的模式,这个Nuget可以以最佳方式管理发生的所有事件。
我至少看到一个问题:
Task.Factory.StartNew(() =>
{
_logger.LogInformation($"[Watching] {location.FileSystemLocation}");
CustomFileSystemWatcher watcher = new CustomFileSystemWatcher(location.FileSystemLocation)
{
//...
};
watcher.Created += OnFileCreated;
});
您正在声明一个局部变量,该变量将在下次运行时由 GC 收集,从而有效地停止文件观察器,将其分配给
FileServiceSynchronizer
字段:
public class FileServiceSynchronizer : ISynchronizer
{
private CustomFileSystemWatcher _watcher;
public void SetUp()
{
_watcher = new CustomFileSystemWatcher(location.FileSystemLocation)
// ...
}
}
这里也没有理由使用
TaskFactory
。