I'm using FileSystemWatcher to upload files to an Azure Blob and I ran into a problem when a bunch of files were created at the same time, resulting in connection issues as every file gets a new connection to the Azure storage account.
I followed the following to group multiple files created at about the same time and execute them inside one storage account connection: Using FileSystemWatcher with multiple files
Of course I changed it a bit as I need multiple actions to be executed for each folder that's being watched.
This creates and starts the FileSystemWatcher.
private void StartFileSystemWatcher(FolderSettings folder) {
DirectoryInfo dir = new DirectoryInfo(folder.Path);
// Checks whether the folder is enabled and
// also the directory is a valid location
if (folder.Enabled && dir.Exists) {
FileSystemWatcher fileSWatch = new FileSystemWatcher();
fileSWatch.Filter = folder.Filter;
fileSWatch.Path = folder.Path;
List<int> actionToExecute = folder.Actions;
fileSWatch.IncludeSubdirectories = folder.Recursive;
// Subscribe to notify filters
fileSWatch.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;
// Associate the event that will be triggered when a new file
// is added to the monitored folder, using a lambda expression
fileSWatch.Created += (senderObj, fileSysArgs) => FileSWatch_Created(senderObj, fileSysArgs, folder);
fileSWatch.Error += (senderObj, fileSysArgs) => FileSWatch_Error(senderObj, fileSysArgs, folder);
// Begin watching
fileSWatch.EnableRaisingEvents = true;
}
else if (!dir.Exists) {
Console.WriteLine(string.Format("Directory '{0}' does not exist!", dir.FullName));
}
}
There's a 'FolderSettings' object for each folder that's being monitored. It also contains the following:
- ReaderWriterLockSlim rwlock
- System.Timers.Timer ProcessTimer
- List FilePaths
private void FileSWatch_Created(object sender, FileSystemEventArgs e, FolderSettings folder) {
// Set up timer to group events
try {
folder.rwlock.EnterWriteLock();
folder.FilePaths.Add(e.FullPath);
if (folder.ProcessTimer == null) {
// First file, start timer
folder.ProcessTimer = new System.Timers.Timer(2000);
folder.ProcessTimer.Elapsed += (senderObj, elapsedEventArgs) => ProcessQueue(senderObj, elapsedEventArgs, folder);
folder.ProcessTimer.Start();
}
else {
// Subsequent file, reset timer
folder.ProcessTimer.Stop();
folder.ProcessTimer.Start();
}
}
finally {
folder.rwlock.ExitWriteLock();
}
}
The error is being thrown in the below code, even with only one action defined and one file that needs to be acted on:
'The read lock is being released without being held.'
private async void ProcessQueue(object sender, ElapsedEventArgs e, FolderSettings folder) {
List<Task> actionTasks = new List<Task>();
folder.rwlock.EnterReadLock();
try {
List<FolderActions> fActs = listActions.Where(a => folder.Actions.Contains(a.Id)).ToList();
fActs.ForEach(a => actionTasks.Add(a.Execute(folder.FilePaths)));
await Task.WhenAll(actionTasks);
folder.FilePaths.Clear();
}
finally {
if (folder.ProcessTimer != null) {
folder.ProcessTimer.Stop();
folder.ProcessTimer.Dispose();
folder.ProcessTimer = null;
}
folder.rwlock.ExitReadLock();
}
}
Any idea how to fix?