Suspending Threads

D

Dave

Hi all,

I have an application which has some worker threads which often have to stop
and wait for some further information from other threads. These pauses will
often take a long time (a couple of minutes) and I want the thread to be
able to begin work ASAP once the new information arrives so Thread.Sleep
isn't the best solution. In .NET 1.1 I used .Suspend and .Resume to handle
these pauses but in .NET 2.0 these methods are no longer favoured. I have
been using .Sleep(Infinite) and .Interupt instead and this seems to be
working okay, but is this the best way to replace .Suspend and .Resume? Are
there better alternatives available?

Dave
 
G

Guest

Suspend was removed because of the inherent problems with arbitrarily
stopping a thread (e.g. it could suspend the thread in the middle of updating
a complex invariant or while the thread had a lock).

The best way to put a thread into a wait state is to use a synchronization
object. What you describe sounds like you need to awaken a thread on an
event of some sort. A WaitHandle-derived class list ManualResetEvent would
probably fit the bill.

I actually wrote a generalized Thread replacement class that uses an event
to suspend and resume. See
http://msmvps.com/blogs/peterritchi...uspend-has-been-deprecated_2E002E002E00_.aspx
 
D

Dave

It does look like the EventWaitHandle will do the trick but is there any
particular reason I should perform the code changes and use an extra object
when the current code seems to be working? I don't mind if there is a good
reason but if it's just to use a different method there doesn't seem to be
much point.

Dave
 
J

Jon Skeet [C# MVP]

Dave said:
It does look like the EventWaitHandle will do the trick but is there any
particular reason I should perform the code changes and use an extra object
when the current code seems to be working? I don't mind if there is a good
reason but if it's just to use a different method there doesn't seem to be
much point.

"Seems to be working" isn't a good enough guarantee in threading.
Arbitrarily suspending another thread when you don't know what locks it
holds etc is a bad idea (just like aborting them arbitrarily is a bad
idea).

I personally favour Monitor.Wait/Pulse over EventWaitHandle unless I
need some of the extra functionality the latter provides, but both are
certainly better than suspending/resuming the thread - it means the
thread can effectively pause itself when *it* knows it's at an
appropriate point.
 
G

Guest

As Jon says, unless you know why something is working you can't trust it to
continue working in other circumstances.

Windows lets you communicate cross-threads between windows as an
optimization. If it had to check every single communication to see if there
were more than one thread involved it would add too much processing. As a
result, Windows lets you do things that aren't safe. Some cross-thread
communications are fine; but what those are aren't documented so you just
have to resort to least-likelihood-of-failure and not perform cross-thread
window communications at all.

The reasons why it will fail (not when, that's non-deterministic) are
proprietary to a certain degree. It could have something to do with memory
being freed after the send of the message; expecting the send to be
synchronous (blocking) meaning the receiving thread is using memory that was
freed but not yet re-used. It could be that a handle that the receiving
thread is using was freed by the sending thread before it knows the receiving
thread processed it. Etc.

Regardless of exact reasons why, it means all sorts of system-wide,
inter-application logic could affect the resources used in the communication
and randomly and non-deterministically cause a failure.

--
Browse http://connect.microsoft.com/VisualStudio/feedback/ and vote.
http://www.peterRitchie.com/blog/
Microsoft MVP, Visual Developer - Visual C#
 
G

Guest

Opps, wrong detail. Thought I was answering a cross-thread control access
post...

When a thread is suspended with Thread.Suspend, or the Windows'
ThreadTerminate, it doesn't ask the thread if it's doing something that can
be interrupted. The thread could be in the middle of accessing a shared
resource to update a complex invariant (an invariant that cannot be updated
with an atomic action), synchronize access to a shared resource, open a file
denying access to other applications, etc. Date is the classic invariant; it
could involve storage of day, year, month, hour, minute, second, millisecond
and could be implemented in a way where the value of a particular date could
not be updated atomically (e.g. if each part of the data were stored in
different variables). If the thread was suspended after the month was
updated to February but before a day of 30 was updated with 28 it would leave
the invariant corrupt. More serious are locks. The reason .NET 2.0
specifically deprecated Thread.Suspend was because of locks. Under the
covers the CLI synchronizes class constructors. If a thread was suspended
while the thread was in a constructor, an new instantiation of that class
could not be constructed (i.e. would block/hang) until the thread was resumed
or the application was restarted because the thread hadn't returned from the
constructor and release the lock. 99.999% of the time the thread may not be
in a constructor when it get suspended. When a multi-threaded application is
running specific code in two threads at the same time depends on many things
(like system-wide, inter-application processing) that you have no control
over. If your customer had an installation that did tend to make this 1 in a
million scenario happen, the only recourse you'd have would be redesign and
redeploy...

--
Browse http://connect.microsoft.com/VisualStudio/feedback/ and vote.
http://www.peterRitchie.com/blog/
Microsoft MVP, Visual Developer - Visual C#
 
G

Guest

Preferring one synchronization type over another is largely personal
preference; unless, as you say, one offers functionality the other doesn't
offer.

I tend to prefer ManualResetEvent's over Mutexes for general thread
suspension/resumption because the execution of each thread isn't mutually
exclusive of the other, i.e. you don't need the foreground thread to block
depending on what the background thread is doing. Using a mutex could
introduce problems if the background thread didn't use the mutex in a
read-only fashion (introducing a dead-lock). I like events better for this
because the thread causing the event to "set" doesn't care (i.e. doesn't
block) if anyone does anything with the transition of the event.

If you need your foreground thread to block until the background thread does
something in particular (i.e. they're mutually exclusive for a particular
time) then Mutex is the better choice. If that were the case, I would tend
to question why a background thread is being used if "particular time" was
more than a couple instructions. Even then, ManualResetEvent could fit the
bill by blocking the foreground thread until the background thread resets the
event. I would tend to separate the suspension/resumption of the thread from
the synchronization...

--
Browse http://connect.microsoft.com/VisualStudio/feedback/ and vote.
http://www.peterRitchie.com/blog/
Microsoft MVP, Visual Developer - Visual C#
 
J

Jon Skeet [C# MVP]

Peter Ritchie said:
Preferring one synchronization type over another is largely personal
preference; unless, as you say, one offers functionality the other doesn't
offer.

True. Monitor.Wait/Pulse is generally faster than ManualResetEvent
(IIRC the test I did a while ago) but it's very unlikely to be
significant.
I tend to prefer ManualResetEvent's over Mutexes for general thread
suspension/resumption because the execution of each thread isn't mutually
exclusive of the other, i.e. you don't need the foreground thread to block
depending on what the background thread is doing. Using a mutex could
introduce problems if the background thread didn't use the mutex in a
read-only fashion (introducing a dead-lock). I like events better for this
because the thread causing the event to "set" doesn't care (i.e. doesn't
block) if anyone does anything with the transition of the event.

Hmm... I tend to use Pulse/Wait in situations where I *do* want
exclusion during the operation - e.g. multiple threads reading from a
producer/consumer queue, which want to wake up and take something off
the queue. You at least want a memory barrier there anyway (which a
lock guarantees) and you only want one thread to be able to try to take
things off the queue (or put it on) at a time.

As you say, it's often personal preference. I'm sure I'm partially
biased towards Wait/Pulse due to previous experience with Java, and I
suspect others may be partially biased towards events as that is the
traditional Win32 way of doing things.
If you need your foreground thread to block until the background thread does
something in particular (i.e. they're mutually exclusive for a particular
time) then Mutex is the better choice. If that were the case, I would tend
to question why a background thread is being used if "particular time" was
more than a couple instructions. Even then, ManualResetEvent could fit the
bill by blocking the foreground thread until the background thread resets the
event. I would tend to separate the suspension/resumption of the thread from
the synchronization...

As I say, often you want them to be very definitely linked - which is
one of the reasons Wait/Pulse is designed the way it is, I suspect.
 
D

Dave

"Seems to be working" isn't a good enough guarantee in threading.
Arbitrarily suspending another thread when you don't know what locks it
holds etc is a bad idea (just like aborting them arbitrarily is a bad
idea).

I agree. That is why I am using Sleep(Timeout.Infinite) and Interrupt
instead of Suspend and Resume. That way the thread has control over when it
enters the wait state directly without the need to create an extra locking
object (such as Monitor or EventWaitHandle). From what I can tell both Peter
Ritchies code and mine have the thread determining where and when to begin
waiting, so presumably the only difference between them would be coder
preference.
 
J

Jon Skeet [C# MVP]

Dave said:
I agree. That is why I am using Sleep(Timeout.Infinite) and Interrupt
instead of Suspend and Resume. That way the thread has control over when it
enters the wait state directly without the need to create an extra locking
object (such as Monitor or EventWaitHandle).

But with the extra cost of interrupting a thread, causing an exception.
Really, do you think a monitor/EventWaitHandle is so expensive as to be
worth avoiding here? Bear in mind that waiting for signals is pretty
much what Auto/ManualReset is there for.
From what I can tell both Peter
Ritchies code and mine have the thread determining where and when to begin
waiting, so presumably the only difference between them would be coder
preference.

No - because unless you've *also* got flags and locking so you know
(for *sure*!) that your thread is actually sleeping, you could well be
interrupting something other than a Sleep call, going back to the same
problem as before.
 
P

Peter Duniho

[...] From what I can tell both Peter
Ritchies code and mine have the thread determining where and when to
begin waiting, so presumably the only difference between them would be
coder preference.

For what it's worth, I don't find that to be the only difference. In
particular, the Interrupt method a) requires that you have a reference to
the Thread object (not always the case) and b) causes an exception to
occur in the thread.

IMHO it's this latter aspect that is the bigger problem. With either of
the other two methods (mutex or event), the code simply sits and waits,
and then continues normally when signaled. With Interrupt, you need to
handle the exception and somehow re-enter the code that should be doing
the work.

So while I agree that all three methods can accomplish essentially the
same thing, I find that using Sleep/Interrupt is significantly inferior to
the other two methods. That is, if the point is simply to introduce a
point at which the code pauses and then resumes, Sleep/Interrupt breaks
that model by forcing the code to exit the block and re-enter later. It's
my opinion that creating a wait object is far less of an issue than
forcing the code into a mold that isn't really appropriate.

I think Sleep/Interrupt would be more useful in situations where you don't
have an infinite sleep time and in which the normal situation is that the
sleep runs for the full time. In that case, using Interrupt to create an
exception would make more sense because, well...interrupting the thread
would actually be the exceptional case in that situation.

For what it's worth, I also don't see mutex and event as being exactly
equivalent, since the event is more a matter of signaling, while the mutex
is an ongoing thing. In other words, I would use an event where I simply
need to wake a thread up and let it continue processing, and a mutex where
I have some resource that needs to be protected while a single thread
operates on it.

Pete
 
D

Dave

Thanks for that Peter.

I changed the code around to use an EventWaitHandle, and while I can't tell
any difference in terms of performance (the code went from taking an
immeasurably small amount of time to taking an immeasurably small amount of
time to run) the code does seem to be a bit "nicer". Now I use WaitOne to
halt execution and Set to resume execution.

Dave
 
P

Peter Duniho

Thanks for that Peter.

I changed the code around to use an EventWaitHandle, and while I can't
tell any difference in terms of performance (the code went from taking
an immeasurably small amount of time to taking an immeasurably small
amount of time to run) the code does seem to be a bit "nicer". Now I
use WaitOne to halt execution and Set to resume execution.

Glad it helped. And yes, I think it would be very unusual to notice a
performance difference between the methods. I can believe that there's a
measurable difference in contrived tests, but I suspect that any
real-world code in which a difference is noticable is a candidate for
fixing so that it doesn't spend so much time switching between running and
not running. :)

Pete
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top