views:

409

answers:

4

So we've all seen the Threading notification on MSDN for many available generic objects:

"Public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe."

My question is, what is it about being an instance variable vs a public static makes it unsafe?

+7  A: 

It's the issue of state. What general makes methods unsafe for multiple threads is they do not access shared state in a thread safe manner. Static methods in general do not access shared state and hence are less likely to run into this problem. It's still possible to have race conditions in static / shared methods if they touch static data but in general static methods do not.

JaredPar
Maybe you should emphasize the fact that it is just 'in general' more.
Dykam
@Dykam, yeah I bolded that part for emphasis
JaredPar
+7  A: 

Nothing inbuilt makes static any more-or-less different (re thread-safety) than instance, except:

  • static methods are often stateless "pure functional" methods, making them automatically thread-safe
  • there is a clear expectation on static members to be thread-safe (since you can't really control what each thread is doing at once) - so it is expected that any static method that could risk thread-safety be made thread-safe

This is not true for instance methods:

  • instance methods commonly access state on that instance
  • there is no expectation of thread safety unless it is made explicit in the documentation

So in general it is expected that the caller manage thread-safety over instances.

There are exceptions where instances are thread-safe (usually for things that are deeply tied to threading, such as a producer-consumer queue) - but IMO any static member that isn't thread safe is a bug.

Marc Gravell
@marc, I humbly disagree... not to say that such does not exist, but I have never had an expectation that static methods are thread-safe, nor have I heard of such an expectation. Static methods can still access static variables, (from any class), as well as instance variables in objects passed in as method parameters.
Charles Bretana
(cont) The only way your expectation could be relied upon is if all static methods were prohibited from using any passed in by-reference variables, or instances of reference types, and from accessing any static variables in that or any other class. This would seem to be an extraordinarily onerous restriction, just to allow your "expectation" to be reliable.
Charles Bretana
You reasoning doesn't follow. I am talking about its own static state (such as a static dictionary/list on a static field, used by the static method), since that cannot be controlled by the caller. Normally static methods are thread-safe by the simple trick of not using any private state ;-p The state of things like `ref` or ref-type args *is* controlled by the caller, and should be managed as such. It likewise doesn't impact external calls. The expectation **is** there, otherwise you would have a **lot** of locking in almost every line of your C# code.
Marc Gravell
@marc, It is the use of shared public state that makes a block of code potentially unsafe, not private state. Any private local variables are by definition only accessible by the stack frame (thread) that created them. So, clearly, I must not be understanding what you mean by public/private... In any event, if a static method is passed a variable containing state, then it is potentially not thread safe, as another thread can call that static method while a previous thread is "in progress" with some invariant in a transient invalid state. Only local (to the method) variables are safe
Charles Bretana
I mean private to the implementation, not local to the method; for example, a (private) static dictionary that underpins a static method (perhaps as a cache) must be synchronized.
Marc Gravell
@marc, Yes, in yr private static dictionary example, I agree, it must be synchronized. but, again, anything that is not stored in the methods stack frame, whether public, private, instance based or static, is generally susceptible to, (can create) a race condition, and, (if the other race conditons (see my answer) are possible), must be synchronized. But I still don't understand what you mean by "private to the implementation".
Charles Bretana
@Marc, as, obviously, anything declared as static must be scoped at the level of a type (class/struct), it is accessible from any thread. but any variable declared at the level of a type (class/struct) that is instance based is also accessible from any thread that has a reference to that instance, whether it is a public or private variable...
Charles Bretana
Yes, and that gets entirely back to my point; there isn't the same expectation of safety around *instance* state. If you have an instance available to multiple threads, you *expect* (unless told otherwise) to have to synchronize access yourself before calling methods. With static methods, the default position is reversed, and you *don't* expect to have to manually synchronize access to a static method. Some instances (typically closely tied to threading) are thread-safe, but they are the minority.
Marc Gravell
Why is the expectation different, when as we seem to agree, the static method, (and whatever state it manipulates), is equally accesible to multiple threads,. and therefore just as potentially unsafe? It is the shared menory, and the existence, and nature of the invariant which depends on that memory, that should be the determinent, not whether the method is instance-based or static.
Charles Bretana
Static state is **by definition** available to all threads (except for `[ThreadStatic]`). Instances are *generally* localised to one thread. I wouldn't expect instances of "average class Jo" to be thread-safe, for all sorts of reasons involving performance, sheer unnecessary code, untested threading code (which is just as dangerous as non-existant synchronization). Threading and synchrnoization needs to happen *carefully*, but only on those classes that will be realistically impacted.
Marc Gravell
But rather than going around in circles; what would you propose? Should "class Person { public string Name {get;set;}}" be made fully synchronized? Where does it end? Better to handle instance synchronization separately (or side-step with immutability).
Marc Gravell
Hey Mark, btw immutable instances are always thread safe, right?
Joan Venge
Yes; true immutability makes things thread safe.
Marc Gravell
@marc, When you say that "instances are generally localized to one thread", what is this based on? is this statement not dependant on how a client uses the instance? As well as where and how client code passes arounf reference(s) to that instance?
Charles Bretana
Oh absolutely; but **most of the time** an object is only really accessed by one thread. When you have multiple threads accessing the same object then it gets fun and you have to start synchronizing - but the vast majority of .NET code does *not* have this problem to deal with. Hence the *generally* (note italics).
Marc Gravell
+4  A: 

This is only true in general.

In general static methods are static because they are not dependant on nor do they access any instance defined data that another thread could also access. In general, the only variables they use are local variables, declared and scoped to the stack frame of that instance of the method. If they did, they could not be static. An Instance method, in contrast, does access some data element (property or field) of the instance.

If, otoh, a static method accesses a static property or field of the class, it is equally non-thread -safe.

There are four conditions needed for a race to be possible.

  1. The first condition is that there are memory locations that are accessible from more than one thread. Typically, these locations are global/static variables or are heap memory reachable from global/static variables.
  2. The second condition is that there is a property (often called an invariant), which is associated with these shared memory locations that must be true, or valid, for the program to function correctly. Typically, the property needs to hold true before an update occurs for the update to be correct.
  3. The third condition is that the invariant property does not hold during some part of the actual update. (It is transiently invalid or false during some portion of the processing).
  4. The fourth and final condition that must occur for a race to happen is that another thread accesses the memory while the invariant is broken, thereby causing inconsistent or incorrect behavior.
Charles Bretana
A: 

The problem with methods that are not thread safe is concurrent access to shared resources such as instance variables. If a static method only works on private/local data, it is inherently thread safe. However, there is no guarantee that static methods do that - this must be done explicitly.

Thus for a static method to be thread safe it cannot access static members without using synchronization and it should copy whatever data it receives as input before modifying it.

Brian Rasmussen
Why is static private data for static methods threadsafe?
Dykam
I am not talking about private as in private members of the type, but private as in local. Because each thread has its own stack where locales are stored these are not shared among different threads and are thus inherently thread safe.
Brian Rasmussen