16

I have an app, listening for the *.log file in a chosen folder. I used FileSystemWatcher.

But there is a problem. The other app responsible for making that file takes following steps:

  1. Make a *.gz file
  2. Unpack it to txt file (some random file name)
  3. Change the *.txt name to proper one with *.log extension.

And I can not change this behaviour.

So I made 2 FileSystemWatchers for *.gz, and *.txt files. Why? Because this app sometimes doesn't unpack the gz file, and sometimes doesn't rename the txt file to the final *.log file.

FileSystemWatcher2 catches txt file, then (in the most cases it is renamed to log in the next 1000ms) I need to wait some time to check if txt file exists (if not, it seems to be renamed to the final *.log file).

The question is, how to check if file exists without Thread.Sleep() to prevent UI freeze?

I hope it is clear, if not I will try to describe it better. I think this is a complex problem.

Some code sample:

Watcher for gz file:

private void fileSystemWatcher_Created(object sender, FileSystemEventArgs e)
{
   //this is for gz files in case if gz file is not unpacked automatically by other app
   //I need to wait and check if gz was unpacked, if not, unpack it by myself,
   //then txt watcher will catch that
   Thread.Sleep(5000);
   if (File.Exists(e.FullPath))
   {
      try
      {
         byte[] dataBuffer = new byte[4096];
         using (System.IO.Stream fs = new FileStream(e.FullPath, 
                                                     FileMode.Open, 
                                                     FileAccess.Read))
         {
            using (GZipInputStream gzipStream = new GZipInputStream(fs))
            {                            
               string fnOut = Path.Combine(path_to_watcher, 
                                           Path.GetFileNameWithoutExtension(e.FullPath));

               using (FileStream fsOut = File.Create(fnOut))
               {
                  StreamUtils.Copy(gzipStream, fsOut, dataBuffer);
               }                            
            }
         }
      }
      catch { //Ignore }
   }
}

Watcher for txt file:

private void fileSystemWatcher2_Created(object sender, FileSystemEventArgs e)
{
   //this is for txt file
   Thread.Sleep(3500);
   if (File.Exists(e.FullPath))
   {
      //make my actions
   }
   else
   {
      //make my actions
   }
}
Marcus Mangelsdorf
  • 2,852
  • 1
  • 30
  • 40
user1750355
  • 316
  • 2
  • 4
  • 10
  • All `Thread.Sleep()` does is r̶e̶l̶i̶n̶q̶u̶i̶s̶h̶ ̶t̶h̶e̶ ̶t̶h̶r̶e̶a̶d release the computer resources it is using so that they can be used for other tasks. It's not ideal for some scenarios (because it has a 1 ms minimum granularity) but for what you're trying to accomplish, it might work just fine. – Robert Harvey Feb 11 '13 at 16:33
  • @RobertHarvey Do you have some documentation for this? From my experience, Thread.Sleep stops a thread from processing any further instructions until the time has elapsed. From MSDN: `The thread will not be scheduled for execution by the operating system for the amount of time specified.` – CodingGorilla Feb 11 '13 at 16:36
  • @CodingGorilla: Yes, that's right. It blocks, and releases the resources the thread is holding for other tasks. It's basically saying "I'm not doing anything for the next n milliseconds, so you can go off and do something else during that time." – Robert Harvey Feb 11 '13 at 16:38
  • @RobertHarvey But if that thread is the UI thread, the UI thread will not continue to process messages from the UI, and hence the UI would freeze. Granted, its not taking any CPU time (is that what you meant?). – CodingGorilla Feb 11 '13 at 16:40
  • @CodingGorilla: Sleeping the UI thread wouldn't make much sense. Presumably the OP is using another thread for the `FileSystemWatcher`. Actually the `FileSystemWatcher` raises events, so there might already be another thread. – Robert Harvey Feb 11 '13 at 16:42
  • @RobertHarvey His original question was: Question is, how to check if file exists without `Thread.Sleep()` to prevent UI freeze? – CodingGorilla Feb 11 '13 at 16:43
  • 1
    @CodingGorilla: Don't overthink this. The pattern is always the same: if you don't want to block your UI, you have to either start a new thread or make the operation asynchronous (essentially the same thing, but one waits in a look while the other receives an event). If you don't want that thread to hog resources while it's looping waiting for something, you have to sleep the thread. – Robert Harvey Feb 11 '13 at 16:45

4 Answers4

17

Actually FileSystemWatcher Created event called in separate thread by .NET itself.. So basically you need to do absolutely nothing. Your code is OK as it is.

Here is the proof:

class Program
{
    static void Main(string[] args)
    {
        FileSystemWatcher fw = new FileSystemWatcher(@"C:\temp");
        fw.Created += fileSystemWatcher_Created;

        Console.WriteLine(Thread.CurrentThread.ManagedThreadId);

        fw.EnableRaisingEvents = true;

        Console.ReadLine();
    }

    static void fileSystemWatcher_Created(object sender, FileSystemEventArgs e)
    {
        Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
    }
}
Boppity Bop
  • 9,613
  • 13
  • 72
  • 151
3

You can use sleep without locking the UI thread, you essentially sleep a seperate thread.

public event EventHandler FileCreated;
public void CheckFileExists()
{
  while(!File.Exists(""))
  {
    Thread.Sleep(1000); 
  }
  FileCreated(this, new EventArgs());
}

Then call this as:

Thread t = new Thread(CheckFileExists);
this.FileCreated += new EventHandler(somemethod);
t.Start();

public void SomeMethod(object sender, EventArgs e)
{
  MessageBox.Show("File Created!");
}

Or as mentioned in another post use a BackGroundWorker in place of the seperate thread, put the code I mentioned into DoWork and listen for OnFinish event but seeing as there isn't that much work to do either methods are fine.

LukeHennerley
  • 6,344
  • 1
  • 32
  • 50
3

You can use a BackGroundWorker to monitor the file system - and avoid freezing your UI

Dave Bish
  • 19,263
  • 7
  • 46
  • 63
0
int i = 0;
while (!File.Exists && i < 30) //limit the time to whait to 30 sec
{
    Threed.Sleep(1000);
    File.Refresh();
    i++;
}
xskxzr
  • 12,442
  • 12
  • 37
  • 77