473,399 Members | 3,401 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,399 software developers and data experts.

FileSystemWatcher question

Hello,

I've got a FileSystemWatcher object setup to check for new files in a
folder. I've never done much with threading, but I think I mght need to here.
When I find a new file there are times when I need to do some time consuming
tasks. When this happens, as it is now, the FileSystemWatcher doesn't seem to
see all of the files if I move a large batch of files in the watch folder at
once. Could someone give me a push in the right direction? Thanks for any
help, I really appreciate it.

Thanks,
Nick
Nov 19 '08 #1
7 2546
public class FileProcessor
{
private object SyncRoot = new object();
private Queue<stringQueue = new Queue<string>;
private bool Terminated = false;

public FileProcessor()
{
var threadStart = new ThreadStart(Execute);
new Thread(threadStart).Start();
}

public void QueueFile(string fileName)
{
lock (SyncRoot)
{
Queue.Enqueue(fileName);
Monitor.Pulse(SyncRoot);
}
}

private void Execute()
{
while (!Terminated)
{
string fileName = null;
lock(SyncRoot)
{
if (Queue.Count 0 && !Terminated)
fileName = Queue.Dequeue();
}

if (fileName != null)
ProcessFile(fileName);
else
Monitor.WaitFor(SyncRoot);
}
}

public void Terminate()
{
Terminated = true;
lock (SyncRoot)
{
Monitor.Pulse(SyncRoot);
}
}

}
Something like that anyway. Probably wont even compile, but you never know
your luck :-)


--
Pete
====
http://mrpmorris.blogspot.com
http://www.capableobjects.com

Nov 19 '08 #2
On Wed, 19 Nov 2008 15:04:35 -0800, Peter Morris
<mr*********@spamgmail.comwrote:
Something like that anyway. Probably wont even compile, but you never
know your luck :-)
Nope, it won't. And even if it did, it would throw an exception. But you
were _really_ close. :)

Here's how I might have done it, were I to implement a class like that
(note that other than fixing the compile and run-time errors, I also
changed the termination condition so as to simplify the class a bit, and
added a possible implementation for the "ProcessFile()" method) (also,
more comments at the end):

public class FileProcessor
{
private object SyncRoot = new object();
private Queue<stringqueue = new Queue<string>;

public FileProcessor()
{
new Thread(Execute).Start();
}

public void QueueFile(string fileName)
{
if (fileName == null)
{
throw new ArgumentNullException("fileName");
}

lock (SyncRoot)
{
queue.Enqueue(fileName);
Monitor.Pulse(SyncRoot);
}
}

private void Execute()
{
lock (SyncRoot)
{
while (true)
{
if (queue.Count 0)
{
string fileName = queue.Dequeue();

if (fileName == null)
{
break;
}

ProcessFile(fileName);
}
else
{
Monitor.Wait(SyncRoot);
}
}

}
}

public void Terminate()
{
lock (SyncRoot)
{
queue.Enqueue(null);
Monitor.Pulse(SyncRoot);
}
}

// Subscribe to the FileFound event to be able to process filenames
// registered with this class
public event Action<stringFileFound;

private void ProcessFile(string fileName)
{
Action<stringhandler = FileFound;

if (handler != null)
{
handler(fileName);
}
}
}

Of course, I make no guarantees that this will compile either. :) Just
because I fixed some of the previous mistakes, that doesn't mean I didn't
introduce my own.

To the OP: I think the point of the above is that Pete M. is suggesting
that you don't do lengthy processing in your event handler(s) for the
FileSystemWatcher event(s), but rather just queue filenames to a separate
consumer class and let it handle things as it can. The above would allow
that.

Keep in mind, however, that while doing this should improve things, by
keeping the FileSystemWatcher class ready and available to monitor other
changes, it simply cannot be relied upon to give 100% notification for
every change that you are trying to look for. Part of your processing
when the FileSystemWatcher class notifies you of a change should be to
look for other changes in the same directory, in case changes came too
quickly for the FileSystemWatcher class to notice them all (it relies on a
fixed-sized buffer than if filled before additions can be processed will
cause some notifications to be lost).

So even with a producer/consumer setup as Pete M. suggests, you are not
guaranteed 100% reliable behavior. But you should be able to get a lot
closer than if you are doing lengthy processing in your FileSystemWatcher
event handlers.

Pete
Nov 19 '08 #3
Many thanks to both of you for your help. I'll give this a try.

"Peter Duniho" wrote:
On Wed, 19 Nov 2008 15:04:35 -0800, Peter Morris
<mr*********@spamgmail.comwrote:
Something like that anyway. Probably wont even compile, but you never
know your luck :-)

Nope, it won't. And even if it did, it would throw an exception. But you
were _really_ close. :)

Here's how I might have done it, were I to implement a class like that
(note that other than fixing the compile and run-time errors, I also
changed the termination condition so as to simplify the class a bit, and
added a possible implementation for the "ProcessFile()" method) (also,
more comments at the end):

public class FileProcessor
{
private object SyncRoot = new object();
private Queue<stringqueue = new Queue<string>;

public FileProcessor()
{
new Thread(Execute).Start();
}

public void QueueFile(string fileName)
{
if (fileName == null)
{
throw new ArgumentNullException("fileName");
}

lock (SyncRoot)
{
queue.Enqueue(fileName);
Monitor.Pulse(SyncRoot);
}
}

private void Execute()
{
lock (SyncRoot)
{
while (true)
{
if (queue.Count 0)
{
string fileName = queue.Dequeue();

if (fileName == null)
{
break;
}

ProcessFile(fileName);
}
else
{
Monitor.Wait(SyncRoot);
}
}

}
}

public void Terminate()
{
lock (SyncRoot)
{
queue.Enqueue(null);
Monitor.Pulse(SyncRoot);
}
}

// Subscribe to the FileFound event to be able to process filenames
// registered with this class
public event Action<stringFileFound;

private void ProcessFile(string fileName)
{
Action<stringhandler = FileFound;

if (handler != null)
{
handler(fileName);
}
}
}

Of course, I make no guarantees that this will compile either. :) Just
because I fixed some of the previous mistakes, that doesn't mean I didn't
introduce my own.

To the OP: I think the point of the above is that Pete M. is suggesting
that you don't do lengthy processing in your event handler(s) for the
FileSystemWatcher event(s), but rather just queue filenames to a separate
consumer class and let it handle things as it can. The above would allow
that.

Keep in mind, however, that while doing this should improve things, by
keeping the FileSystemWatcher class ready and available to monitor other
changes, it simply cannot be relied upon to give 100% notification for
every change that you are trying to look for. Part of your processing
when the FileSystemWatcher class notifies you of a change should be to
look for other changes in the same directory, in case changes came too
quickly for the FileSystemWatcher class to notice them all (it relies on a
fixed-sized buffer than if filled before additions can be processed will
cause some notifications to be lost).

So even with a producer/consumer setup as Pete M. suggests, you are not
guaranteed 100% reliable behavior. But you should be able to get a lot
closer than if you are doing lengthy processing in your FileSystemWatcher
event handlers.

Pete
Nov 20 '08 #4
Hi Pete

Any chance you could explain a few things?
private object SyncRoot = new object();
private Queue<stringqueue = new Queue<string>;

public FileProcessor()
{
new Thread(Execute).Start();
}
Yeah, I was tempted to do the same thread start but couldn't remember if I
needed ThreadStart or not, and I wasn't willing to start up VS :_)

public void QueueFile(string fileName)
{
if (fileName == null)
{
throw new ArgumentNullException("fileName");
}
hehe. You work too hard :-) For newsgroups I just can't be bothered with
stuff like this, it's bad enough that I have to write it in my real code let
alone newsgroups. I suppose I should try to be a perfectionist in NG source
as well as my own, but at the end of the day I think the person reading has
the responsibility to learn some good techniques of their own rather than
responders being responsible for teaching them.
private void Execute()
{
lock (SyncRoot)
{
while (true)
This is where I am starting to disagree with you :-)

{
if (queue.Count 0)
{
string fileName = queue.Dequeue();

if (fileName == null)
{
break;
That's it, we are now officially in disagreement :-) Why would you go for
such a vague way of terminating the thread rather than my crystal clear
"Terminated" member?

Other than the change to how I terminate the thread + the check for null I
don't see how yours is functionally different from mine, what were your
thoughts at the time of reading my source?
// Subscribe to the FileFound event to be able to process filenames
// registered with this class
public event Action<stringFileFound;

private void ProcessFile(string fileName)
{
Action<stringhandler = FileFound;

if (handler != null)
{
handler(fileName);
}
}
}
I think I see now! You are showing him how to handle ProcessFile! There
was just no way I was going to bother :-)

To the OP: I think the point of the above is that Pete M. is suggesting
that you don't do lengthy processing in your event handler(s) for the
FileSystemWatcher event(s), but rather just queue filenames to a separate
consumer class and let it handle things as it can. The above would allow
that.
Exactly. In my service that does something similar I have a class that
decides what to do with the file based on its location + extension. It goes
into one of three of these queues based on how long the operation takes

Fast - Typically moves the file elsewhere
Intermediate - Typically unzips a small file
Long - Generates a MySQL db and zips it

So that the long process doesn't hold up the stuff that should occur
immediately despite the fact that there is a long term operation in
progress.
FileSystemWatcher class to notice them all (it relies on a fixed-sized
buffer than if filled before additions can be processed will cause some
notifications to be lost).
Any idea what size this buffer is? For me there will be a maximum of 10
people connecting at once so I doubt I will need to be concerned, but it's
always interesting to know.

--
Pete
====
http://mrpmorris.blogspot.com
http://www.capableobjects.com

Nov 20 '08 #5
On Thu, 20 Nov 2008 09:37:53 -0800, Peter Morris
<mr*********@spamgmail.comwrote:
Hi Pete

Any chance you could explain a few things?
> private object SyncRoot = new object();
private Queue<stringqueue = new Queue<string>;

public FileProcessor()
{
new Thread(Execute).Start();
}

Yeah, I was tempted to do the same thread start but couldn't remember if
I needed ThreadStart or not, and I wasn't willing to start up VS :_)
Since C# 2.0, the compiler will correctly infer the necessary delegate
type and create it for you.

Your approach isn't wrong, I just prefer the more concise syntax.
>
> public void QueueFile(string fileName)
{
if (fileName == null)
{
throw new ArgumentNullException("fileName");
}

hehe. You work too hard :-) For newsgroups I just can't be bothered
with stuff like this,
Understandable. To each his own. Note, however, to some extent I put
that in to make it very clear that, with my change to the termination
condition, the _only_ way a null should be able to get into the queue is
via the Terminate() method. The same thing would apply to your own code
as well, though the consequences of a mistake would be less drastic in
your version (the processing thread could pause prematurely, whereas in my
mine it would just exit altogether).
[...]
> {
if (queue.Count 0)
{
string fileName = queue.Dequeue();

if (fileName == null)
{
break;

That's it, we are now officially in disagreement :-) Why would you go
for such a vague way of terminating the thread rather than my crystal
clear "Terminated" member?
Why do you say it is vague? There's no ambiguity at all here.
Other than the change to how I terminate the thread + the check for null
I don't see how yours is functionally different from mine, what were
your thoughts at the time of reading my source?
I've removed a variable for one. With the removal of that variable, not
only have I saved as many as 4 bytes from the size of my class, I have
avoided the risk of making the mistake you did: failing to either put the
use of that variable inside a synchronized block of code, or making the
variable "volatile". :)

Another maintainance advantage is that by putting the termination
condition in the queue itself, I can then keep the "wait or dequeue" code
completely separate from the "dequeue then process" code. In your
version, those two functions of the block of code are intertwined. In
mine, they are clearly delineated, separate from each other.

Assuming one fixes your bug, there is no _functional_ difference between
the two. Both techniques would successfully allow the thread to exit.
But, I find my way simpler and thus more maintainable. The fewer
variables you have, the fewer chances you have to mess up how you use
those variables, and I like to be able to understand the code in smaller
pieces where possible.

I admit that the difference is relatively minor (assuming the other
approach is implemented correctly ;) ), and I wouldn't spend a lot of time
trying to argue in favor of one or the other. But hopefully the above
adequately describes why I do in fact have a preference of one over the
other.
> // Subscribe to the FileFound event to be able to process filenames
// registered with this class
public event Action<stringFileFound;

private void ProcessFile(string fileName)
{
Action<stringhandler = FileFound;

if (handler != null)
{
handler(fileName);
}
}
}

I think I see now! You are showing him how to handle ProcessFile!
There was just no way I was going to bother :-)
lol...well, in that case you just guarantee that your code example
wouldn't compile. :)
[...]
>FileSystemWatcher class to notice them all (it relies on a fixed-sized
buffer than if filled before additions can be processed will cause
some notifications to be lost).

Any idea what size this buffer is? For me there will be a maximum of 10
people connecting at once so I doubt I will need to be concerned, but
it's always interesting to know.
No, I don't know. Last time I looked at this, I did take a look to see if
it was documented, but couldn't find any specifics. Just the admonition
to try to process the notifications as quickly as possible. I'm not sure
the exact number would be very useful anyway, since whether the buffer
gets filled depends not only on how fast one processes the notifications,
but how frequently changes are happening to the file system. In addition,
the two relate according to things that are independent of the buffer
size: CPU speed, disk i/o bandwidth, stuff like that.

Pete
Nov 20 '08 #6
On Fri, 21 Nov 2008 02:58:32 -0800, Peter Morris
<mr*********@spamgmail.comwrote:
Hi Pete

>Why do you say it is vague? There's no ambiguity at all here.

In your case I see a null entry in the list as a kind of "magic number"
approach. If the value is null you end, or maybe you would put in the
string "STOP" or "FINISH" or "QUIT". My point is that stopping based on
the value of an entry is vague because what that value should be is
entirely up to the person writing the code, different people would have
different opinions on what that value should be. However, having a
Terminated boolean member to indicated that the Terminate has been
called is obvious, the value will be "true" if terminated, it is
indisputible.

>I've removed a variable for one. With the removal of that variable,
not only have I saved as many as 4 bytes from the size of my class, I
have avoided the risk of making the mistake you did: failing to either
put the use of that variable inside a synchronized block of code, or
making the variable "volatile". :)

Yes, I did miss that, didn't I :-) I'd still rather go with the boolean
approach though :-)
>Assuming one fixes your bug, there is no _functional_ difference
between the two.

Except when you terminate :-)
Ah, I see what you mean. Yes, you're right...there's a slight
difference. Though, I'll point out that one reason it's hard to detect
(you had trouble yourself :) ) is that you've got your "dequeue" mixed up
with your "terminate". :)

That behavior isn't precluded by using a sentinel value (and for the
record, sentinel values are quite common in software...there's nothing
fundamentally wrong with them), but you'd have to use an implementation of
Queue<Tthat allows for inserting at the head (i.e. not .NET's
implementation). You could write your own, but I agree that doing so
_just_ to avoid the flag wouldn't make sense.

Also note that the code you posted doesn't actually behave exactly as you
say. In particular, this statement isn't true:
My approach will stop processing items in the loop immediately,
The code you posted doesn't terminate when you call Terminate(). It
simply goes back and waits again. It won't terminate until the _next_
time the monitor is pulsed (whether that happens due to another call to
Terminate() or someone trying to enqueue something).

Again, I think this illustrates how a simpler design is easier to code
correctly. By my count, we're up to three bugs in your implementation
just related to termination alone. I got my implementation right the
first try, and I don't think you can say it's because I'm a better
programmer. I'm not...I just know better than to complicate things when I
don't have to. :)

Now, if you have to -- that is, you have that specific requirement to
terminate immediately -- I suppose you have to implement it that way. But
neither of us have the actual specification to work with, and it's
entirely possible my version would work fine in this context.
[...]
I'm more of a "here's the general idea, now write it youself" kind of
helper. The best lesson you can learn is how to teach yourself :-)
Well, I agree with that generally. In fact, you'll note that I rarely
actually post actual code. I prefer to just describe a solution, or point
someone to the documentation. But, I'm of the mind that when I do post
code, I should try to get it as close to correct as possible. Just
because I'm not being paid, that doesn't mean I shouldn't take pride in my
work. :)
>>Any idea what size this buffer is? For me there will be a maximum of
10 people connecting at once so I doubt I will need to be concerned,
but it's always interesting to know.

No, I don't know. Last time I looked at this, I did take a look to see
if it was documented, but couldn't find any specifics.

I should be okay with around 10 at the time time, maximum, I hope! This
should definitely be documented, and obvious!
As long as in your case, 10 connections translates directly to no more
than 10 file system changes at once, then I think you're definitely safe.
That's an odd guarantee to be able to have, but if you have it, no
problem. :)

Pete
Nov 21 '08 #7
Thanks again to both of you for the excellent discussion.

"Peter Duniho" wrote:
On Fri, 21 Nov 2008 02:58:32 -0800, Peter Morris
<mr*********@spamgmail.comwrote:
Hi Pete

Why do you say it is vague? There's no ambiguity at all here.
In your case I see a null entry in the list as a kind of "magic number"
approach. If the value is null you end, or maybe you would put in the
string "STOP" or "FINISH" or "QUIT". My point is that stopping based on
the value of an entry is vague because what that value should be is
entirely up to the person writing the code, different people would have
different opinions on what that value should be. However, having a
Terminated boolean member to indicated that the Terminate has been
called is obvious, the value will be "true" if terminated, it is
indisputible.

I've removed a variable for one. With the removal of that variable,
not only have I saved as many as 4 bytes from the size of my class, I
have avoided the risk of making the mistake you did: failing to either
put the use of that variable inside a synchronized block of code, or
making the variable "volatile". :)
Yes, I did miss that, didn't I :-) I'd still rather go with the boolean
approach though :-)
Assuming one fixes your bug, there is no _functional_ difference
between the two.
Except when you terminate :-)

Ah, I see what you mean. Yes, you're right...there's a slight
difference. Though, I'll point out that one reason it's hard to detect
(you had trouble yourself :) ) is that you've got your "dequeue" mixed up
with your "terminate". :)

That behavior isn't precluded by using a sentinel value (and for the
record, sentinel values are quite common in software...there's nothing
fundamentally wrong with them), but you'd have to use an implementation of
Queue<Tthat allows for inserting at the head (i.e. not .NET's
implementation). You could write your own, but I agree that doing so
_just_ to avoid the flag wouldn't make sense.

Also note that the code you posted doesn't actually behave exactly as you
say. In particular, this statement isn't true:
My approach will stop processing items in the loop immediately,

The code you posted doesn't terminate when you call Terminate(). It
simply goes back and waits again. It won't terminate until the _next_
time the monitor is pulsed (whether that happens due to another call to
Terminate() or someone trying to enqueue something).

Again, I think this illustrates how a simpler design is easier to code
correctly. By my count, we're up to three bugs in your implementation
just related to termination alone. I got my implementation right the
first try, and I don't think you can say it's because I'm a better
programmer. I'm not...I just know better than to complicate things when I
don't have to. :)

Now, if you have to -- that is, you have that specific requirement to
terminate immediately -- I suppose you have to implement it that way. But
neither of us have the actual specification to work with, and it's
entirely possible my version would work fine in this context.
[...]
I'm more of a "here's the general idea, now write it youself" kind of
helper. The best lesson you can learn is how to teach yourself :-)

Well, I agree with that generally. In fact, you'll note that I rarely
actually post actual code. I prefer to just describe a solution, or point
someone to the documentation. But, I'm of the mind that when I do post
code, I should try to get it as close to correct as possible. Just
because I'm not being paid, that doesn't mean I shouldn't take pride in my
work. :)
>Any idea what size this buffer is? For me there will be a maximum of
10 people connecting at once so I doubt I will need to be concerned,
but it's always interesting to know.

No, I don't know. Last time I looked at this, I did take a look to see
if it was documented, but couldn't find any specifics.
I should be okay with around 10 at the time time, maximum, I hope! This
should definitely be documented, and obvious!

As long as in your case, 10 connections translates directly to no more
than 10 file system changes at once, then I think you're definitely safe.
That's an odd guarantee to be able to have, but if you have it, no
problem. :)

Pete
Nov 21 '08 #8

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

2
by: Hardy Wang | last post by:
Hi, I have a FileSystemWatcher object to monitor one certain folder on server. FileSystemWatcher watcher = new FileSystemWatcher(); watcher.Path = @"C:\MyFolder"; watcher.NotifyFilter =...
13
by: David | last post by:
I have been working on trying to write a directory watcher service. One of the requirments is that it be able to watch multiple directories, not sub directories of one parent directory, but just...
3
by: Krish | last post by:
Hello Gurus, Pardon me for asking dumb question... I wrote small csharp program to watch a folder for any created text file... When i try to run through debugger and stop after "...
1
by: Terry | last post by:
I have created a Windows Service application which uses FileSystemWatcher to monitor multiple folders and file filters. These folders/files are loaded from a database table. When a Change Event...
20
by: J-T | last post by:
We are working on an asp.net application which is a 3-tier application.I was aksed to create a component which monitors a folder and gets the file and pass them to a class library in our business...
1
by: dinny | last post by:
Question about FileSystemWatcher: How can I determine if a delete notification was a file or a directory. I was using the DirectoryInfo.exists method to determine if I am receiving file or...
9
by: Tushar | last post by:
Followup-To: microsoft.public.dotnet.general Does anyone know when is this event raised, is it: 1) When the file is created but may not have been closed 2) When the file is created AND it has...
3
by: =?Utf-8?B?RGFuaWVs?= | last post by:
I'm working with the FileSystemWatcher which has a Created event. But this event is raised as soon as the new file begins to be written on disk. What I want is a notification after a file being...
5
by: =?Utf-8?B?Sm9obiBT?= | last post by:
I am trying to find out if there is a way to tell if there is already a filesystemwatcher (created by a webservice) monitoring a folder. I have a webservice that creates a filesystemwatcher,...
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.