Safely modifying variables in a multithreaded environment.

  • Thread starter Thread starter Frank Rizzo
  • Start date Start date
F

Frank Rizzo

Hello,

I want to use a variable as a signal to the thread that it should stop
what it's doing (code below). As a rule, should I lock an object only
when I am writing to it or do I have to lock on reading as well? For
instance, consider the following code:

//running in a non-GUI thread
foreach (object o in objects)
{
...
if (SignalObject.StopSignalDetected) break;
}


//running in the GUI thread.
void btnStop_Click(...)
{
lock (SignalObject)
{
SignalObject.StopSignalDetected = true;
}
}

Is SignalObject.StopSignalDetected safe here? Or should I lock it in
the non-GUI thread as well, even though it will only read it?

Thanks
 
Frank Rizzo said:
I want to use a variable as a signal to the thread that it should stop
what it's doing (code below). As a rule, should I lock an object only
when I am writing to it or do I have to lock on reading as well?

In general, you need to lock for reading as well, for two reasons:

1) To pick up changes in writing threads atomically
2) To guarantee that pick up changes in writing threads at all

See http://www.pobox.com/~skeet/csharp/threads/volatility.shtml
 
Usually when you have a number of operations to carry out as one logical unit
that require reading and writing then you should lock the variable, for
example:

Company A requests person A's bank balance (it's $100)
Company B requests person A's bank balance (it's $100)
Company A adds $100 -> total is $200 (because it read $100)
Company B debits $25 -> total is $75 (because it read $100)
Company A saves the value $200 to user A's account
Company B saves the value $75 to user A's account

At the end of the day person A only has $75 in his account instead of $175.

We had one logical unit of work but it took many steps, so for the bank
account which here is a resource that is being competed for we need to lock
so only one thread can access the rsource at any time.

In your case I am guessing SignalObject.StopSignalDetected is just assigning
to a variable or returning a boolean internally, so there are not a number of
steps so you really don't need to lock this variable.

Hope that helps
Mark.
 
Jon said:
In general, you need to lock for reading as well, for two reasons:

1) To pick up changes in writing threads atomically

Jon, great link. I learned a lot. And your article is correct: there is
quite a bit of a performance hit in my app if I lock on reading and
writing. One quick question though. If I just lock on writing and
don't care whether the reading thread reads it the first time (second or
third time is fine), am I ok?
 
Frank Rizzo said:
Jon, great link. I learned a lot. And your article is correct: there is
quite a bit of a performance hit in my app if I lock on reading and
writing.

Is there? Is it truly significant? I believe in *most* real-world cases
the hit isn't worth worrying about. A test case which does nothing but
read and write with multiple threads might have problems, but a real
app which is doing other things won't have problems - usually! It
depends on what the app is doing, of course.
One quick question though. If I just lock on writing and
don't care whether the reading thread reads it the first time (second or
third time is fine), am I ok?

Well, it's not just a case of "second or third time" - it's whether the
reading thread *ever* sees the change. That isn't guaranteed, although
to be honest if you call other methods (which aren't inlined) I believe
there's a memory barrier at that point.
 
Back
Top