kovac
kovac

Reputation: 5389

Better way to implement the following long running thread in C#

I have an application that has a main thread that waits for an event (receiving a file in the directory, for example) and Post that file to an API. This part is working fine.

On the other hand, due to some reasons, it's possible that the main thread misses out some files. So, I have setup a background thread to run throughout application lifetime cleaning up those missed files as shown below:

public virtual void MissedFileHandler()
{
     if (_missedFileHandlerThread == null || !_missedFileHandlerThread.IsAlive)
     {
          _missedFileHandlerThread = new Thread(new ThreadStart(ClearMissedFiles));
          _missedFileHandlerThread.IsBackground = true;
          _missedFileHandlerThread.Start();
     }
}

protected virtual void ClearMissedFiles()
{
     while (true)
     {
         string[] missedFiles = new string[] { };
         missedFiles = Directory.GetFiles(ConfigurationHelper.applicationRootDirectory, "*.txt", SearchOption.TopDirectoryOnly);
         Parallel.ForEach(missedFiless, (currentMissedFile) =>
         {
             bool isFileInUse = true;
             isFileInUse = FileHelper.CheckIfFileInUse(filesInUseCollection, currentMissedFile)
             if (!isFileInUse)
             {
                 bool isHttpTransferSuccess = false;
                 isHttpTransferSuccess = FileHelper.SendFileToApi(userid, currentMissedFile);
             if (isHttpTransferSuccess)
             {
                  File.Delete(currentMissedFile);

             }
         }
     });
     Thread.Sleep(1000);
     }
  }

Please note that running this as a Windows service or a scheduler is not an option for some other reasons. So, I do need a background worker to clean up missed files periodically. I checked out Timer and WaitHandles but not very sure how I can implement the following using those.

The way the two processes (main even handler for new files and the background cleaner is invoked are, in the main application thread I will instantiate the class and invoke the methods at application startup. It's the Thread.Sleep() and the while (true) that is bothering me and am looking for a better approach. Thanks.

Upvotes: 0

Views: 80

Answers (2)

Enigmativity
Enigmativity

Reputation: 117154

Try using Microsoft's Reactive Framework. Then you can do this:

IObservable<Unit> fileChanges =
    Observable
        .Using(() =>
        {
            var fsw = new FileSystemWatcher();
            fsw.Path = path;
            fsw.EnableRaisingEvents = true;
            return fsw;
        }, fsw =>
            Observable
                .FromEventPattern<FileSystemEventHandler, FileSystemEventArgs>(
                    h => fsw.Changed += h,
                    h => fsw.Changed -= h))
        .Select(ep => Unit.Default);

IObservable<Unit> periodically =
    Observable
        .Interval(TimeSpan.FromSeconds(30.0))
        .Select(x => Unit.Default);

IObservable<string> clearMissedFiles =
    fileChanges
        .Merge(periodically)
        .SelectMany(u => Directory.GetFiles(ConfigurationHelper.applicationRootDirectory, "*.txt", SearchOption.TopDirectoryOnly))
        .Where(f => !FileHelper.CheckIfFileInUse(filesInUseCollection, f))
        .SelectMany(f => Observable.Start(() => FileHelper.SendFileToApi(userid, f)), (f, s) => new { file = f, success = s })
        .Where(x => x.success)
        .Select(x => x.file);

IDisposable subscription =
    clearMissedFiles
        .Subscribe(f => File.Delete(f));

This watches the file system and check periodically. And calls the API in parallel and it doesn't cause the O/S grief with trying to delete multiple files at once.

Just call subscription.Dispose() to clean up before exiting.

NuGet "System.Reactive" for the bits and then reference using System.Reactive.Linq for the extensions to show.

Upvotes: 1

burnttoast11
burnttoast11

Reputation: 1184

You can use a file watcher to detect new files in that directory.

private void watch()
{
  FileSystemWatcher watcher = new FileSystemWatcher();

  // Set your path with this
  watcher.Path = path;

  // Subscribe to event
  watcher.Changed += new FileSystemEventHandler(OnChanged);
  watcher.EnableRaisingEvents = true;
}

Upvotes: 3

Related Questions