Search code examples
c#multithreadingfilesystemwatcher

Multithreading with FileSystemWatching


I have this code that is working fine for me:

using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;

namespace Test2
{
    internal static class MyProgram
    {
        [STAThread]
        private static void Main(string[] args)
        {
            List<Plugin> plugins = new List<Plugin>
                                       {
                                           new Plugin(@"c:\temp\file1.txt"),
                                           new Plugin(@"c:\temp\file2.txt"),
                                           new Plugin(@"c:\temp\file3.txt"),
                                       };

            foreach (var plugin in plugins)
            {
                plugin.StartWatch();
                plugin.WaitHandler.WaitOne();
            }
        }
    }


    public class Plugin
    {
        private FileSystemWatcher _watcher;
        private readonly string _file;
        public AutoResetEvent WaitHandler { get; private set; }

        public Plugin(string file)
        {
            _file = file;
            Console.WriteLine("Creating plugin for file {0}", _file);
            WaitHandler = new AutoResetEvent(false);
        }

        public void StartWatch()
        {
            Console.WriteLine("Starting watching {0}", _file);
            WaitHandler.Reset();
            FileInfo fileInfo = new FileInfo(_file);
            if (fileInfo.Directory != null)
            {
                _watcher = new FileSystemWatcher(fileInfo.Directory.FullName, fileInfo.Name);
                _watcher.NotifyFilter = NotifyFilters.LastWrite;
                _watcher.Changed += OnChanged;
                _watcher.EnableRaisingEvents = true;
            }
        }

        private void OnChanged(object source, FileSystemEventArgs e)
        {
            Console.WriteLine("Finished watching file {0}", _file);
            _watcher.Dispose();
            WaitHandler.Set();
        }
    }
}

The result that is printed from the code is the following (which is working fine and is important):

Creating plugin for file c:\temp\file1.txt
Creating plugin for file c:\temp\file2.txt
Creating plugin for file c:\temp\file3.txt
Starting watching c:\temp\file1.txt
Finished watching file c:\temp\file1.txt
Starting watching c:\temp\file2.txt
Finished watching file c:\temp\file2.txt
Starting watching c:\temp\file3.txt
Finished watching file c:\temp\file3.txt

The problem i have is that i want to make my program multi-threaded and have another thread running in the background. i cannot do it with this design because the main thread is blocked by the AutoResetEvent. The other thread that i want to add is a thread which will listen to a file named "AbortFlagFile.txt". once "AbortFlagFile.txt" is modified than i want my program to be aborted. I was thinking maybe to create a PluginEndEvent which will be fired after OnChange() is finished.

It is very important for me that the program will produce the logs in the same order as i wrote above Thanks!


Solution

  • Declare an array of waithandles and a counter

    private static WaitHandle[] waithandles;
    private static int waitcount;
    

    In constructor, initialize it

    waithandles = new WaitHandle[3] { new AutoResetEvent(false), new AutoResetEvent(false) };
    

    you will also need a function to increment the thread counter

    private static void incThreadCounter()
    {
            lock (sobj)
        {
        waitcount++;
        }
    }
    

    in the code where you initialize your thread, call the incThreadCounter()

    I actually do mine as a callback after the thread has been spooled up. Prevents false counts

    in the area where you have your autoreset set a while loop, and decrement the thread count as they signal

    while (waitcount > 0)
    {
        WaitHandle.WaitAny(waithandles, 5000);
        waitcount--;
    }
    

    When the count is 0, you proceed to the next part, which may be spooling up additional threads

    If you want to make sure thread 2 runs after thread 1, you will want to use a variation of the logic above. In fact, if thread 2 always needs to run after thread 1 and thread 3 always needs to run after thread 2, then you don't really need multi-threading. Threading is only valuable if there are processes that can run simultaneously.

    Hope this helps