more static + thread safety

P

Peter K

Say I have this class

public class Simple
{
private string name;

public string Name
{
get { return name; }
set { name = value; }
}
}

Is it "thread safe"? Multiple threads can certainly access their own
instances of Simple without problems...


But what if I have another class which has a "static reference" to Simple:

public class StaticReference
{
private static Simple simple = new Simple();

public static Simple
{
get { return simple; }
}
}


Now if I have multiple threads accessing StaticReference then they can
upset each other by altering the Name property on the "simple" instance
they share, correct? So is "StaticReference" thread-safe?




Thanks,
Peter
 
J

Jon Skeet [C# MVP]

Say I have this class

public class Simple
{
private string name;

public string Name
{
get { return name; }
set { name = value; }
}

}

Is it "thread safe"?

No - not that that's necessarily a bad thing in itself. Most classes
don't need to be thread-safe, even if they're used in multi-threaded
environments. Instead, the classes using them need to take account of
the multi-threaded nature.
Multiple threads can certainly access their own instances of Simple
without problems...

Yes, but that's not what makes something thread-safe.

But what if I have another class which has a "static reference" to Simple:

public class StaticReference
{
private static Simple simple = new Simple();

public static Simple
{
get { return simple; }
}

}

Now if I have multiple threads accessing StaticReference then they can
upset each other by altering the Name property on the "simple" instance
they share, correct? So is "StaticReference" thread-safe?

StaticReference itself is thread-safe, but Simple isn't - classes
using StaticReference.Simple would need to take appropriate action to
ensure that they always see the most recent value of Name. This is
usually done through locking around any access to shared data.

See http://pobox.com/~skeet/csharp/threads for more information.

Jon
 
P

Peter K

No - not that that's necessarily a bad thing in itself. Most classes
don't need to be thread-safe, even if they're used in multi-threaded
environments. Instead, the classes using them need to take account of
the multi-threaded nature.


Yes, but that's not what makes something thread-safe.

Ok. Is it more then that when multiple threads access an object they
share, the threads can rely on that any data they read from the object is
in a consistent state - ie that updates to data in the object will always
be "finished" before reads can be performed; and that updates can't be
performed until in-progress reads are complete?

(But the threads can't really know of course where the data came from if
they share an object. For example, thread A could set name to "xxx" and
thread B could then set name to "yyy". Thread A would then also see
"yyy" as name. This is really a logic problem I guess).

StaticReference itself is thread-safe, but Simple isn't - classes
using StaticReference.Simple would need to take appropriate action to
ensure that they always see the most recent value of Name. This is
usually done through locking around any access to shared data.

See http://pobox.com/~skeet/csharp/threads for more information.

Yes, thanks. So either Simple would need to ensure "thread-safety", or
the threads accessing the shared Simple would need to. And under any
circumstance, the threads would need to be aware that they share a Simple
object and could affect each other through the Name property.

/Peter
 
J

Jon Skeet [C# MVP]

Ok. Is it more then that when multiple threads access an object they
share, the threads can rely on that any data they read from the object is
in a consistent state - ie that updates to data in the object will always
be "finished" before reads can be performed; and that updates can't be
performed until in-progress reads are complete?

That's part of it. The other part is the volatility side of things -
making sure that not only is no other thread trying to change stuff,
but that you see all the changes which have already been made, despite
processor caches etc.
(But the threads can't really know of course where the data came from if
they share an object. For example, thread A could set name to "xxx" and
thread B could then set name to "yyy". Thread A would then also see
"yyy" as name. This is really a logic problem I guess).

It might be a problem or it might not, depending on the situation :)
Yes, thanks. So either Simple would need to ensure "thread-safety", or
the threads accessing the shared Simple would need to. And under any
circumstance, the threads would need to be aware that they share a Simple
object and could affect each other through the Name property.

With a single property, it's easy to make Simple thread-safe in itself
- but often you need multiple operations to be synchronized. Even with
a single property that could be the case.

For instance, suppose I want to run something which changes everything
with a name of "Fred" to "Bill". If you do:

Simple simple = StaticReference.Simple;
if (simple.Name=="Fred")
{
simple.Name = "Bill";
}

then another thread could change the name from "Fred" to "Joe" between
the test and the set.

Jon
 
P

Peter Duniho

Peter said:
[...]
Ok. Is it more then that when multiple threads access an object they
share, the threads can rely on that any data they read from the object is
in a consistent state - ie that updates to data in the object will always
be "finished" before reads can be performed; and that updates can't be
performed until in-progress reads are complete?

No, that's not true. If one thread is reading from some data while
another thread is writing to the data, you need to ensure some
synchronization occurs. There are potential problems with a variety of
things, including reads in the middle of a write, reads from data that
is still in the cache of some other CPU, and reordering of instructions
causing reads and writes intended to be in a specific order to not be in
that order.

For "small" data (things the CPU can handle in a single instruction),
using the "volatile" keyword will ensure the ordering/caching issues are
dealt with, and of course because the data can be dealt with atomically,
partial reads/writes aren't an issue.

For larger amounts of data, you need to use some kind of locking
mechanism. Jon's "threads" page discusses this aspect in more detail,
among other things.

As far as I know, if you use some kind of explicit locking, you don't
need "volatile", but Jon and yet a third "Peter" are currently
discussing whether that's actually true in another thread. As far as I
recall, no decision has been finalized yet. :)
(But the threads can't really know of course where the data came from if
they share an object. For example, thread A could set name to "xxx" and
thread B could then set name to "yyy". Thread A would then also see
"yyy" as name. This is really a logic problem I guess).

Yes, that's a separate problem. The stuff I'm talking about above
assumes that the threads are in fact supposed to be sharing the data.
If you have a data structure that's not supposed to be shared, but the
threads are sharing it anyway, well...that's a different problem. :)
Yes, thanks. So either Simple would need to ensure "thread-safety", or
the threads accessing the shared Simple would need to. And under any
circumstance, the threads would need to be aware that they share a Simple
object and could affect each other through the Name property.

Yes on all points. To emphasize the last one: yes, even if access to
the Simple class is made thread-safe, the threads still need to account
for the fact that they all have access to a shared data structure.

Pete
 
P

Peter Duniho

Peter said:
Say I have this class

public class Simple
{
private string name;

public string Name
{
get { return name; }
set { name = value; }
}
}

Is it "thread safe"? Multiple threads can certainly access their own
instances of Simple without problems...

And just in case Jon's reply wasn't already clear enough:

As long as each thread has its own instance of Simple to use, then there
are no problems (as you already point out). But "thread-safe" means
that even if a single instance was shared between different threads,
data integrity would be ensured (note that this is different from
ensuring that the threads are cooperating :) ).

From your reply to Jon, it appears to me that you already understand
this now, but I figure it doesn't hurt to make sure.

Pete
 
P

Peter K

For instance, suppose I want to run something which changes everything
with a name of "Fred" to "Bill". If you do:

Simple simple = StaticReference.Simple;
if (simple.Name=="Fred")
{
simple.Name = "Bill";
}

then another thread could change the name from "Fred" to "Joe" between
the test and the set.

Right, thanks for the "food for thought". I can see that even with this
ultra-simple example all sorts of issues can crop up.

Even if Simple is thread-safe and StaticReference is thread-safe,
operations involving these classes may not be. (I think that's what you're
getting at).

So to ensure complete thread-safety it would be necessary to encapsulate
all operations involving Simple in a "SimpleOperations" class, and lock
over the complex operations there.

/Peter
 
P

Peter Duniho

Peter said:
Even if Simple is thread-safe and StaticReference is thread-safe,
operations involving these classes may not be. (I think that's what you're
getting at).

Jon can clarify, but I'd say the answer to that is "no". By definition,
if the classes are "thread-safe" then operations involving those classes
are "thread-safe".

The classes can't be "thread-safe" unless all uses of the class are
thread-safe.

To further complicate matters, a class can have thread-safe members,
without the class itself being thread-safe. This would happen if
specific members were protected in a thread-safe way, but others were not.
So to ensure complete thread-safety it would be necessary to encapsulate
all operations involving Simple in a "SimpleOperations" class, and lock
over the complex operations there.

The Simple class itself could be made thread-safe. You don't need an
extra class, necessarily. Usually, a class that properly encapsulates
its implementation should be able to be made thread-safe without
introducing a new class (at least, not to the client of the class...you
may in fact need new data and/or classes in the implementation of
course, though in many cases this is as simple as adding an instance of
an object for locking purposes, and then adding the necessary "lock"
statements).

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