2 different threads checking on same variable scenario

D

David

I am trying to get this straight in my head so I can implement.

I wrote a small utility some time ago in c# that utilized a handful of
threads. That scenario was one main thread manually spawning a handfull of
worker threads, then waiting for all of them to complete before moving on. I
believe I used array of ManualResetEvents and the WaitAll() method to do
this. All the worker threads used a shared object (a common place to
collect and output data). I recall having to use 'lock' (essentially the
monitor class) to synchronize access to this object. All works great. That
was a while ago and now I have a new scenario (much simpler, I think) but I
can't get a solution straight in my head. I'm using this past example
because it is my only point of reference for mutlithreaded scenarios.

now, I'm writting a small utility (console app) that is using the asynch
programming pattern. I simply want the main thread to show some kind of
progress indicator (writing dots to screen, whatever) after calling an
asynch method and then move on after asnych operation completes. So
currently the code is structured so that the asynch operation is started and
the main thread just waits using WaitOne() until the asynch operation
signals the manualResentEvent. Now I want to implement the progress
indicator but I'm not sure how. I'm assuming I need to get rid of the
manualResetEvent and waitOne() setup and move to checking some variable that
both threads have access to?

while (sharedVar) {do progress indicator thing}

Based on what I learned from the older project I described, I immediately
thought to just use lock. But then I thought that the lock would never be
released from the progress indicator loop in order for the other thread to
gain the lock and modify the sharedVar so the loop could end?

any input on how I should be handling this scenario would be greatly
appreciated.
 
A

Alberto Poblacion

David said:
while (sharedVar) {do progress indicator thing}

If the sharedVar is a boolean, you don't need any kind of lock. Assigning
a value to a bolean variable and testing the value of a boolean variable are
thread-safe operations.
 
J

Jon Skeet [C# MVP]

If the sharedVar is a boolean, you don't need any kind of lock. Assigning
a value to a bolean variable and testing the value of a boolean variable are
thread-safe operations.

Not true, unless sharedVar is declared to be volatile. If it's not
volatile, there's nothing to stop the reading thread from caching the
value and never rereading it from main memory.

I'd either make it volatile, or make it a property which took out a
lock for reading and writing.

Jon
 
D

David

thanks Alberto,
thats reminds me of some of the info I got during the last project... is
this because reading/writing boolean value is an 'atomic' operation?
Wondering if I'm remembering correctly. I think the same place that I
learned of atomic operations also talked about using 'volatile' keyword when
declaring the variables to be used like this? Do I need to declare my bool
shardVar in any special way?

Thanks again, I appreciate it. I looked into this stuff some time ago but
havn't used it since... I'm just putting together a really small utility
that I need to do fast so I was really hoping to not have to go through all
the research I did last time. You are saving me a lot of time. Thank you.
 
D

David

whoa, quick responses on this thread. Thanks!
I replied before seeing Jon's reply. Jon answered my question about
declaring it 'specially'. Use volatile.

thanks Jon.
 
J

Jon Skeet [C# MVP]

thats reminds me of some of the info I got during the last project... is
this because reading/writing boolean value is an 'atomic' operation?

Yes, it's atomic, but it's not volatile unless you declare it to be.
Wondering if I'm remembering correctly. I think the same place that I
learned of atomic operations also talked about using 'volatile' keyword when
declaring the variables to be used like this? Do I need to declare my bool
shardVar in any special way?

See http://pobox.com/~skeet/csharp/threads/volatility.shtml

Jon
 
D

David

thanks Jon. I remember now, I have your page on my browser favorites... it
helped me before. I'll revew it again.
 
W

Willy Denoyette [MVP]

David said:
I am trying to get this straight in my head so I can implement.

I wrote a small utility some time ago in c# that utilized a handful of
threads. That scenario was one main thread manually spawning a handfull of
worker threads, then waiting for all of them to complete before moving on.
I believe I used array of ManualResetEvents and the WaitAll() method to do
this. All the worker threads used a shared object (a common place to
collect and output data). I recall having to use 'lock' (essentially the
monitor class) to synchronize access to this object. All works great. That
was a while ago and now I have a new scenario (much simpler, I think) but
I can't get a solution straight in my head. I'm using this past example
because it is my only point of reference for mutlithreaded scenarios.

now, I'm writting a small utility (console app) that is using the asynch
programming pattern. I simply want the main thread to show some kind of
progress indicator (writing dots to screen, whatever) after calling an
asynch method and then move on after asnych operation completes. So
currently the code is structured so that the asynch operation is started
and the main thread just waits using WaitOne() until the asynch operation
signals the manualResentEvent. Now I want to implement the progress
indicator but I'm not sure how. I'm assuming I need to get rid of the
manualResetEvent and waitOne() setup and move to checking some variable
that both threads have access to?

while (sharedVar) {do progress indicator thing}

Based on what I learned from the older project I described, I immediately
thought to just use lock. But then I thought that the lock would never be
released from the progress indicator loop in order for the other thread to
gain the lock and modify the sharedVar so the loop could end?

any input on how I should be handling this scenario would be greatly
appreciated.


No need for a shared variable, just keep synchronizing both threads as you
did, but use the WaitOne overhead that takes a TimeSpan. Set the Timespan to
a reasonable value depending on the duration of the other threads task,
update the user interface (the progress indicator) each time WaitOne
times-out.

Willy.
 
D

David

interesting... I'll check out that overload. Do you see this as just
'another' way to accomplish this or are there certain pros/cons that may
make one a better choice than the other (shared var vs waitOne overload with
timespan)? Without looking yet the waitOne with timespan overload sounds
like it may be more fitting for my particular need (progress indicator)... I
was probably going to just use the shared var to control the loop and use
console.writeline()'s and short thread.sleep()'s within the loop.
 
P

Peter Duniho

interesting... I'll check out that overload. Do you see this as just
'another' way to accomplish this or are there certain pros/cons that may
make one a better choice than the other

To me, it depends on what you intend to do in your main thread's loop.

Because you are implementing a console application here, it seems to me
that a third way to implement this would be to simply have the main thread
call some "do work" function repeatedly. That function would make as much
progress toward the goal as you want for each "update" (where an "update"
may just be writing a new "." to the console output), would return to the
caller so it can handle the "update" and when called again would resume
where it left off.

This would be especially appropriate if the work you need to do is already
conveniently broken into multiple chunks (for example, it involves a loop
that processes a list of files), where the the top-level loop in your main
thread can handle managing the list of chunks to process (that way the "do
work" function doesn't even need to maintain any state between calls...the
caller is handling that for it).

Depending on just what you intended to do in your "do progress indicator
thing" loop, there may be a fourth option. That is, use an auto-reset
wait event to block the main thread between progress updates, a flag to
indicate when the work has been done, and then have the worker thread set
the flag when it's done, and intermittently set the wait event to indicate
to the main thread to update the progress.

To summarize, here's four different ways I see as having been mentioned in
this thread:

#1:
volatile bool fDone = false;

// start worker thread

while (!fDone)
{
Sleep(...);
// update progress
}

In the worker thread, fDone is set when the work has been completed.

#2:
AutoResetEvent event = new AutoResetEvent(false);

// start worker thread

while (!event.WaitOne(new TimeSpan(0, 0, 1), false))
{
// update progress
}

In the worker thread, the event is set when the work has been completed.

#3:
AutoResetEvent event = new AutoResetEvent(false);
volatile bool fDone = false;

// start worker thread

while (!fDone)
{
event.WaitOne();
// update progress
}

In the worker thread, the event is set when the main thread should update
the progress indication, and the fDone flag is set when the work is done..

#4:
foreach (WorkItem item in WorkCollection)
{
item.DoWork();
// update progress
}

There's no worker thread. The main thread just iteratively performs work,
updating progress between calls to the method that does work. You may or
may not have exactly the above data structure design. For example,
instead of having a class that encapsulates the work items, you might just
have some list of objects (strings containing filenames, for example), and
a method in some other class (your main Program class, for example) that
does some work on a single object (takes a filename and does some
processing on that file, for example).

To compare and contrast the various methods:

The first two provide progress updates on a strictly time-based
interval. The upside is that you always have some feedback to the user.
The downside is that feedback doesn't really indicate what progress has
been made; in fact, the worker thread could completely lock up and you'd
still show progress as being made.

Between the first two, I don't see too much difference. They both use
slightly different mechanisms to accomplish very much the same thing.

The second two provide progress updates that are tied to the actual
work being done. The upside is that you have direct indication of the
progress being made. The downside is that the feedback may or may not be
provided in a timely manner. Both of the second pair do rely on the work
being done having regular "checkpoints" at which it makes sense to update
the progress.

Between the second pair, probably the biggest difference is that with
option #4, you don't even have to make a new thread. It does rely on the
main thread having more involvement with the work being done, however. If
you want greater encapsulation, option #3 is probably more desirable.

This is not even a complete analysis. As you can see, there's a wide
variety of approaches to doing what you want. They are roughly equivalent
in that they all get the job done, but they all have different specific
behaviors, and different demands on the architecture of the part of your
code that actually does work.

I hope I have helped, rather than further confusing the issue. I realize
sometimes all someone wants is to be told "do it this way"...I even find
myself in that position sometimes. :)

Pete
 
D

David

thanks Pete. That is really great information. Very helpful. I'm saving this
thread for future reference.

interesting... I'll check out that overload. Do you see this as just
'another' way to accomplish this or are there certain pros/cons that may
make one a better choice than the other

To me, it depends on what you intend to do in your main thread's loop.

Because you are implementing a console application here, it seems to me
that a third way to implement this would be to simply have the main thread
call some "do work" function repeatedly. That function would make as much
progress toward the goal as you want for each "update" (where an "update"
may just be writing a new "." to the console output), would return to the
caller so it can handle the "update" and when called again would resume
where it left off.

This would be especially appropriate if the work you need to do is already
conveniently broken into multiple chunks (for example, it involves a loop
that processes a list of files), where the the top-level loop in your main
thread can handle managing the list of chunks to process (that way the "do
work" function doesn't even need to maintain any state between calls...the
caller is handling that for it).

Depending on just what you intended to do in your "do progress indicator
thing" loop, there may be a fourth option. That is, use an auto-reset
wait event to block the main thread between progress updates, a flag to
indicate when the work has been done, and then have the worker thread set
the flag when it's done, and intermittently set the wait event to indicate
to the main thread to update the progress.

To summarize, here's four different ways I see as having been mentioned in
this thread:

#1:
volatile bool fDone = false;

// start worker thread

while (!fDone)
{
Sleep(...);
// update progress
}

In the worker thread, fDone is set when the work has been completed.

#2:
AutoResetEvent event = new AutoResetEvent(false);

// start worker thread

while (!event.WaitOne(new TimeSpan(0, 0, 1), false))
{
// update progress
}

In the worker thread, the event is set when the work has been completed.

#3:
AutoResetEvent event = new AutoResetEvent(false);
volatile bool fDone = false;

// start worker thread

while (!fDone)
{
event.WaitOne();
// update progress
}

In the worker thread, the event is set when the main thread should update
the progress indication, and the fDone flag is set when the work is done.

#4:
foreach (WorkItem item in WorkCollection)
{
item.DoWork();
// update progress
}

There's no worker thread. The main thread just iteratively performs work,
updating progress between calls to the method that does work. You may or
may not have exactly the above data structure design. For example,
instead of having a class that encapsulates the work items, you might just
have some list of objects (strings containing filenames, for example), and
a method in some other class (your main Program class, for example) that
does some work on a single object (takes a filename and does some
processing on that file, for example).

To compare and contrast the various methods:

The first two provide progress updates on a strictly time-based
interval. The upside is that you always have some feedback to the user.
The downside is that feedback doesn't really indicate what progress has
been made; in fact, the worker thread could completely lock up and you'd
still show progress as being made.

Between the first two, I don't see too much difference. They both use
slightly different mechanisms to accomplish very much the same thing.

The second two provide progress updates that are tied to the actual
work being done. The upside is that you have direct indication of the
progress being made. The downside is that the feedback may or may not be
provided in a timely manner. Both of the second pair do rely on the work
being done having regular "checkpoints" at which it makes sense to update
the progress.

Between the second pair, probably the biggest difference is that with
option #4, you don't even have to make a new thread. It does rely on the
main thread having more involvement with the work being done, however. If
you want greater encapsulation, option #3 is probably more desirable.

This is not even a complete analysis. As you can see, there's a wide
variety of approaches to doing what you want. They are roughly equivalent
in that they all get the job done, but they all have different specific
behaviors, and different demands on the architecture of the part of your
code that actually does work.

I hope I have helped, rather than further confusing the issue. I realize
sometimes all someone wants is to be told "do it this way"...I even find
myself in that position sometimes. :)

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