System.Timers.Timer, thread synchronization issues

  • Thread starter Thread starter Tony Gravagno
  • Start date Start date
T

Tony Gravagno

I have a class that instantiates two Timer objects that fire at
different intervals. My class can be instantiated within a Windows
Form or from a Windows Service. Actions performed by one of the event
handlers may take longer than the interval for either of the timers,
so it's possible for multiple events to fire "simultaneously" and for
events to queue up. I'm attempting to get the timers to sync on some
reference type object, or use System.Threading.Monitor to stop them
from re-entering code that's already being executed.

When a timer event fires I use Monitor.TryEnter(refobj) to attempt a
lock, and simply return on failure (in other words, don't bother
trying to do anything, we're already doing something). The refobj is
an instance of another one of my classes, but this doesn't appear to
be a suitable sync object.

I've tried making the object static/shared so the same object is
consistent across threads. Using this/me doesn't work. I can't use a
this/me reference to a Windows Form because the code could be
instantiated from a Service.

I am using similar code on methods invoked from the container - if a
timer event is doing some operation within the instance, the container
is informed that the object is busy, rather than making it wait for a
turn. If I can't lockdown the object then timers and user requests
are in contention, and the rest of the code outside of these entry
points is not thread-safe.

What type of object do I use to ensure Monitor.Enter(object) and
related methods will be respected by any thread? All of the classic
examples on the Net assume we can use 'this' or 'Me' and that we're
running in a Windows Form. This isn't valid for Windows Services,
especially when events like OnPause and OnContinue may get run from a
different thread than the one that started the process.

Thanks!
 
Tony said:
I have a class that instantiates two Timer objects that fire at
different intervals. My class can be instantiated within a Windows
Form or from a Windows Service. Actions performed by one of the event
handlers may take longer than the interval for either of the timers,
so it's possible for multiple events to fire "simultaneously" and for
events to queue up. I'm attempting to get the timers to sync on some
reference type object, or use System.Threading.Monitor to stop them
from re-entering code that's already being executed.

When a timer event fires I use Monitor.TryEnter(refobj) to attempt a
lock, and simply return on failure (in other words, don't bother
trying to do anything, we're already doing something). The refobj is
an instance of another one of my classes, but this doesn't appear to
be a suitable sync object.

I've tried making the object static/shared so the same object is
consistent across threads. Using this/me doesn't work. I can't use a
this/me reference to a Windows Form because the code could be
instantiated from a Service.

I am using similar code on methods invoked from the container - if a
timer event is doing some operation within the instance, the container
is informed that the object is busy, rather than making it wait for a
turn. If I can't lockdown the object then timers and user requests
are in contention, and the rest of the code outside of these entry
points is not thread-safe.

What type of object do I use to ensure Monitor.Enter(object) and
related methods will be respected by any thread? All of the classic
examples on the Net assume we can use 'this' or 'Me' and that we're
running in a Windows Form. This isn't valid for Windows Services,
especially when events like OnPause and OnContinue may get run from a
different thread than the one that started the process.

Thanks!

First off, this/me has nothing at all to do with windows forms. It is
a reference to the current object instance. So, it is valid inside of
a service class - it's valid inside any class (well any instance
method anyway, it isn't valid inside of shared methods since there is
no instance to refer to). In other words, it is perfectly valid to
pass a me reference inside of a windows service to Monitor.TryEnter.
You should keep in mind that you want to keep locks as small as
possible since they can impact performance. You only need a lock when
accessing shared resources, such as instance variables inside a class -
and then only if the access is read-write. Generally, you don't want
to make the lock object shared (unless the resource being protected is
shared) - because that will cause you to lock every instance of the
class and that is generally not what you want, since usually the
resource that you are protecting is local to that instance.

As for the question about what is appropriate to pass to the various
Monitor methods - well, any reference type will do. Don't use anything
that is a structure - it must be a reference type.
 
Please don't misinterpret this as lack of gratitude for your time, but
none of your response helped considering I spelled out the issues in
the OP. Maybe the following clarifications will help.

Tom Shelton said:
First off, this/me has nothing at all to do with windows forms. It is
a reference to the current object instance. So, it is valid inside of
a service class - it's valid inside any class (well any instance
method anyway, it isn't valid inside of shared methods since there is
no instance to refer to).

That's all correct.
In other words, it is perfectly valid to
pass a me reference inside of a windows service to Monitor.TryEnter.

It's valid, but it doesn't work, that's why I'm here. Just to
clarify, my class is instantiated from a service or other container,
and my class encapsulates the Timer - the service does not instantiate
the timer, so this/me for the service isn't in the scope of the timer
unless I pass it in (which may be necessary the way this is going).

Almost all examples use a timer within Windows Forms where this/me
refers to a Windows Forms object which can be used as a
SynchronizingObject. Here is the text from MSDN and it seems everyone
references this text:
When the Elapsed event is handled by a visual Windows Forms
component, such as a button, accessing the component through the
system-thread pool might result in an exception or just might not
work. Avoid this effect by setting SynchronizingObject to a Windows
Forms component, which causes the method that handles the Elapsed
event to be called on the same thread that the component was created
on.

It seems so many people who describe timers fall back on this example
that it's become impossible for me to find an example that doesn't use
a Windows Forms component for synchronization. Well, when we're not
running a GUI we don't have a Windows Forms component to use, unless
we just instantiate one just for the purpose, which seems silly. What
I'm finding is that this/me does no good unless we sync on such a
component. I even created my own implementation of ISynchronizeInvoke
and instantiated an object from there to sync on - all events get
marshalled through my proxy but everything I use in
Monitor.TryEnter(whatever) still fails.

As another example, here's another hint at the problem from a blog,
one of many where this sort of issue is mentioned:
The server-based timer is designed for use with worker threads
in a multithreaded environment. Server timers can move among threads
to handle the raised Elapsed event, resulting in more accuracy
(metronome-quality) than Windows timers. The System.Timers.Timer class
will call the timer event handler on a worker thread obtained from the
common language runtime (CLR) thread pool. When using the
System.Timers.Timer, be sure to set the appropriate Principal to the
thread that will execute the Elapsed event because this worker thread
won't inherit the Principal from the main thread.

I'm trying to do what's stated there, by trying to find some reference
type that can be used as a sync object, the above mentioned
"Principal". But it seems no matter what I use there are worker
threads being used that are not using true references back to the main
instance. So my Monitor.Enter(whatever) is failing because "whatever"
is different on some of the threads spawned in the threadpool.

You should keep in mind that you want to keep locks as small as
possible since they can impact performance. You only need a lock when
accessing shared resources, such as instance variables inside a class -
and then only if the access is read-write.

Full agreement. The application code does a lot of things including
communications, handling of single-threaded events from encapsulated
objects, etc - I need to synchronize timer events or all of this falls
apart with threads resetting status values at the wrong time. This
isn't supposed to be a multi-threaded app, but the behaviour of the
Timer is forcing me to recode it like it is.

Generally, you don't want
to make the lock object shared (unless the resource being protected is
shared) - because that will cause you to lock every instance of the
class and that is generally not what you want, since usually the
resource that you are protecting is local to that instance.

In this case it Is what I want. I want a timer that will tick off
time. Understanding it may tick while I'm still doing something, I
want to throw away the events that I don't need. If Monitor.TryEnter
fails then I can throw away the tick. Unfortunately, while you're
saying "that will cause you to lock every instance of the class", I
only have one class instance and some of the events (not all!) are
still getting through the TryEnter.

As for the question about what is appropriate to pass to the various
Monitor methods - well, any reference type will do.

That simply isn't correct. "Any reference type will do" isn't
working, which is what I stated in the OP. As indicated above, I've
instantiated a number of different kinds of reference types, not
structures, that still don't lock out all events. It seems evident
that System.Windows.Forms.Form objects have something that is needed
here that I haven't figured out yet. I haven't yet tried to create a
dummy class that derives from Forms.Control which has an hWnd property
- maybe that's what this thing needs, that's what the trick was before
..NET. As I said above, I think that's silly.
Don't use anything
that is a structure - it must be a reference type.

Agreed.

Thanks again for your time.
 
Actually, a lot of people advise *not* locking on "me"/"this", because it
allows external callers to lock on the object, causing possible deadlock
scenarios that were not anticipated.

*ANY* reference type can be used as a lock object. As long as each thread
has access to the same reference (and you don't keep changing that
reference), then it will lock correctly. Depending on the scenario, you
might want to use a static lock object (i.e. "private readonly object
SyncLock = new object();", or you might want to pass a lock object to your
thread when spawning it.

If you can't (for whatever reason) pass the sync-lock (or an encapsulating
class) around, then another solution is to use a named Mutex or similar.

Marc
 
oops - missed the key keyword... just imagine the "static" was there all
along...

Marc
 
Problem solved: The VS2005 debugger has a tendency to skip breakpoints
when events queue up. When one-stepping through code, we can expect
control will jump to the event handler on events, but then we'll
usually see it go back to where we originally were - in a
multi-threaded app we're used to this. What I was seeing was that
control would jump to the event handler and stay there, and then
one-stepping would continue into the Monitor block which I thought was
failing. What's really happening is that even if you're using the F11
one-step, all code between the current line and the event handler will
get executed without the debugger walking through it. More break
points might have shown this earlier.

Is that a bug or a feature? Any setting to ensure that "Step Into"
works through ALL code?

I proved that the sync-lock was working by putting an always
incrementing counter at the beginning of my event handler. Then I put
another counter that incremented once past the Monitor.TryEnter and
decremented on the Monitor.Exit. I have a test to see if that second
counter is ever greater than one and it was not. The first counter
seemed to increment properly whether I saw the event or not.

Yes, "this/me" will work. Sorry for the confusion. I've posted snips
of my code below for anyone else who needs to implement something like
this.
HTH

Thanks again.


Marc Gravell said:
Actually, a lot of people advise *not* locking on "me"/"this", because it
allows external callers to lock on the object, causing possible deadlock
scenarios that were not anticipated.

*ANY* reference type can be used as a lock object. As long as each thread
has access to the same reference (and you don't keep changing that
reference), then it will lock correctly. Depending on the scenario, you
might want to use a static lock object (i.e. "private readonly object
SyncLock = new object();", or you might want to pass a lock object to your
thread when spawning it.

If you can't (for whatever reason) pass the sync-lock (or an encapsulating
class) around, then another solution is to use a named Mutex or similar.

Marc

oops - missed the key keyword... just imagine the "static"
was there all along...


The object assigned to System.Timers.Timer.SynchronizingObject must
implement ISynchronizeInvoke, so you can't just sync the timer itself
on anything. Not knowing any class that implements this I created my
own, and while it does marshal requests it wasn't necessary for this.
Here is some skeleton code in C# but the same base should work with
VB.

class MyClass()
{
private SyncronizationObject SyncObject =
new SyncronizationObject();
// that's my implementation of ISynchronizationObject
private static Object SyncLock = new Object();
private static int concurrent = 0; // testing only
private static int events = 0; // testing only
...
}

void Init()
{
tTimer = new System.Timers.Timer();
tTimer.SynchronizingObject = SyncObject;
// That's only a proxy between the event and the handler,
// it can be removed.
tTimer.Elapsed += new
System.Timers.ElapsedEventHandler(tTimer_Elapsed);
tTimer.Interval = 5000;
tTimer.AutoReset = true;
tTimer.Enabled = true;
}

void tTimer_Elapsed(
object sender, System.Timers.ElapsedEventArgs e)
{
events += 1;
if (!Monitor.TryEnter(SyncLock)) { return; }
try
{
concurrent += 1;
if (concurrent > 1)
{
throw new Exception("Concurrency issue1");
// this event was never thrown,
// proof that locking was working
}
HandleTransaction();
}
finally
{
concurrent -= 1;
if (concurrent > 1)
{
throw new Exception("Concurrency issue2");
}
Monitor.Exit(SyncLock);
}
}

The first time the event fires, TryEnter is true and allows execution
of the following code block. The next few times the event fires, the
TryEnter fails as it should. After a while, as I'm walking through
code in debug in VS2005, it looks like events start coming through the
TryEnter like a freight train. What's happening is that VS2005 just
doesn't show all of the code being handled in method
HandleTransaction, so it just looks like that code is being handled in
different threads.
 
Back
Top