179

Are there overall rules/guidelines for what makes a method thread-safe? I understand that there are probably a million one-off situations, but what about in general? Is it this simple?

  1. If a method only accesses local variables, it's thread safe.

Is that it? Does that apply for static methods as well?

One answer, provided by @Cybis, was:

Local variables cannot be shared among threads because each thread gets its own stack.

Is that the case for static methods as well?

If a method is passed a reference object, does that break thread safety? I have done some research, and there is a lot out there about certain cases, but I was hoping to be able to define, by using just a few rules, guidelines to follow to make sure a method is thread safe.

So, I guess my ultimate question is: "Is there a short list of rules that define a thread-safe method? If so, what are they?"

EDIT
A lot of good points have been made here. I think the real answer to this question is: "There are no simple rules to ensure thread safety." Cool. Fine. But in general I think the accepted answer provides a good, short summary. There are always exceptions. So be it. I can live with that.

John Smith
  • 7,243
  • 6
  • 49
  • 61
Bob Horn
  • 33,387
  • 34
  • 113
  • 219
  • 66
    Thou shalt not access variables that are also accesseth by other threads without a locketh. – Hans Passant Mar 24 '12 at 00:32
  • 4
    Hanth pathant hath turned into an igor! – Martin James Mar 24 '12 at 13:04
  • 3
    Also.. 'Thou shalt not access variables that are also accesseth by other threads without a locketh' - unleth it dothent matter that much if the value read is ocasionally not the latest or is actually grossly incorrect. – Martin James Mar 24 '12 at 13:08
  • [Here](https://blogs.msdn.microsoft.com/ericlippert/2009/10/19/what-is-this-thing-you-call-thread-safe/) is a nice blog by Eric to take you into a whirlwind. – RBT Jun 06 '17 at 02:03

4 Answers4

162

If a method (instance or static) only references variables scoped within that method then it is thread safe because each thread has its own stack:

In this instance, multiple threads could call ThreadSafeMethod concurrently without issue.

public class Thing
{
    public int ThreadSafeMethod(string parameter1)
    {
        int number; // each thread will have its own variable for number.
        number = parameter1.Length;
        return number;
    }
}

This is also true if the method calls other class method which only reference locally scoped variables:

public class Thing
{
    public int ThreadSafeMethod(string parameter1)
    {
        int number;
        number = this.GetLength(parameter1);
        return number;
    }

    private int GetLength(string value)
    {
        int length = value.Length;
        return length;
    }
}

If a method accesses any (object state) properties or fields (instance or static) then you need to use locks to ensure that the values are not modified by a different thread:

public class Thing
{
    private string someValue; // all threads will read and write to this same field value

    public int NonThreadSafeMethod(string parameter1)
    {
        this.someValue = parameter1;

        int number;

        // Since access to someValue is not synchronised by the class, a separate thread
        // could have changed its value between this thread setting its value at the start 
        // of the method and this line reading its value.
        number = this.someValue.Length;
        return number;
    }
}

You should be aware that any parameters passed in to the method which are not either a struct or immutable could be mutated by another thread outside the scope of the method.

To ensure proper concurrency you need to use locking.

for further information see lock statement C# reference and ReadWriterLockSlim.

lock is mostly useful for providing one at a time functionality,
ReadWriterLockSlim is useful if you need multiple readers and single writers.

John Smith
  • 7,243
  • 6
  • 49
  • 61
Trevor Pilley
  • 16,156
  • 5
  • 44
  • 60
  • 19
    In the third example `private string someValue;` is not `static` so each instance will get a separate copy of that variable. So can you please explain how this is not thread safe? – Bharadwaj Apr 03 '14 at 13:25
  • 36
    @Bharadwaj if there is one instance of the `Thing` class accessed by multiple threads – Trevor Pilley Apr 03 '14 at 13:51
119

If a method only accesses local variables, it's thread safe. Is that it?

Absolultely not. You can write a program with only a single local variable accessed from a single thread that is nevertheless not threadsafe:

https://stackoverflow.com/a/8883117/88656

Does that apply for static methods as well?

Absolutely not.

One answer, provided by @Cybis, was: "Local variables cannot be shared among threads because each thread gets its own stack."

Absolutely not. The distinguishing characteristic of a local variable is that it is only visible from within the local scope, not that it is allocated on the temporary pool. It is perfectly legal and possible to access the same local variable from two different threads. You can do so by using anonymous methods, lambdas, iterator blocks or async methods.

Is that the case for static methods as well?

Absolutely not.

If a method is passed a reference object, does that break thread safety?

Maybe.

I've done some research, and there is a lot out there about certain cases, but I was hoping to be able to define, by using just a few rules, guidelines to follow to make sure a method is thread safe.

You are going to have to learn to live with disappointment. This is a very difficult subject.

So, I guess my ultimate question is: "Is there a short list of rules that define a thread-safe method?

Nope. As you saw from my example earlier an empty method can be non-thread-safe. You might as well ask "is there a short list of rules that ensures a method is correct". No, there is not. Thread safety is nothing more than an extremely complicated kind of correctness.

Moreover, the fact that you are asking the question indicates your fundamental misunderstanding about thread safety. Thread safety is a global, not a local property of a program. The reason why it is so hard to get right is because you must have a complete knowledge of the threading behaviour of the entire program in order to ensure its safety.

Again, look at my example: every method is trivial. It is the way that the methods interact with each other at a "global" level that makes the program deadlock. You can't look at every method and check it off as "safe" and then expect that the whole program is safe, any more than you can conclude that because your house is made of 100% non-hollow bricks that the house is also non-hollow. The hollowness of a house is a global property of the whole thing, not an aggregate of the properties of its parts.

Community
  • 1
  • 1
Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • You said: "You can't look at every method and check it off as 'safe' and then expect that the whole program is safe." Agreed. But that's actually not what I'm asking. I'm hoping to at least understand that, yeah, this method is doing things correctly as far as thread-safety goes. Sure, some *other* method may make things thread-unsafe, but at least method A looks good from that perspective. – Bob Horn Mar 24 '12 at 11:55
  • 16
    Core statement: _Thread safety is a global, not a local property of a program._ – Greg D Mar 24 '12 at 13:18
  • You also said: "It is perfectly legal and possible to access the same local variable from two different threads." Does anyone know where there might be an example of this? I'd like to see that to understand it better. – Bob Horn Mar 24 '12 at 16:17
  • 5
    @BobHorn: `class C { public static Func getter; public static Action setter; public static void M() { int x = 0; getter = ()=>x; setter = y=>{x=y;};} }` Call M(), then call C.getter and C.setter on two different threads. The local variable can now be written to and read from on two different threads even though it is a local. Again: the defining characteristic of a local variable is that it is *local*, not that it is *on the stack of the thread*. – Eric Lippert Mar 24 '12 at 16:20
  • 1
    Okay, say maybe I wasn't on the same page as far as what a *local* variable means. I meant a local variable as scoped to the method only. In your example, getter and setter are *class* level variables. – Bob Horn Mar 24 '12 at 16:44
  • @BobHorn, but the point is that `x` is a local variable and you can access it from two different threads. What else could being a local variable mean? – svick Mar 24 '12 at 17:20
  • 1
    @svick, Okay, I see what you mean. You guys are right. It's semantics at this point. Yes, x is a local variable, but it's being used with class variables, which wasn't my original intent when mention local-only variables. I didn't want this to end up as all of the esoteric ways that things *could* go wrong. But, I guess that's the nature of thread safety: it's not simple. – Bob Horn Mar 24 '12 at 17:43
  • 1
    @BobHorn: OK, if you don't like that one, then: make an async method that has a local. Write to the local on one thread. Do an await, and resume the async method on another thread. Read the local. And now you have a local that is written on one thread and read on another. Now in that case at least you don't have the problem of the local being written and read *at the same time* on different threads. My point is: being *local* just makes a variable *accessible by its name* only from within *the text of the method*. That's what "local" means. – Eric Lippert Mar 24 '12 at 22:28
  • 1
    LOL. It's not that I don't like it. You're right. There's no doubt. I mean, you're Eric Lippert, and I'm Bob Horn. The problem is that I won't have the problem you describe because I won't do that. But, you're right; that needs to be said so people don't think they have thread safety when you can't say that with 100% certainty. – Bob Horn Mar 24 '12 at 23:19
  • 4
    @BobHorn: I think it has more to do with understanding our tools on a level such that we can speak about them with authority and knowledge. E.g., the original question betrayed a lack of understanding about what a local variable is. This error is quite common, but the fact is that it is an error and should be corrected. The definition of a local variable is quite precise and should be treated accordingly. :) This isn't an answer for "people" who don't get that pathological cases exist. These are a clarification so that you can understand what you actually asked. :) – Greg D Mar 25 '12 at 03:59
  • 9
    @EricLippert: The MSDN documentation for many classes declares that 'Public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.' or sometimes 'This type is thread safe.' (e.g. String), how can these guarantees be made when thread-safety is a global concern? – Mike Zboray Mar 25 '12 at 06:59
  • 3
    @mikez: Good question. Either those methods do not mutate any state, or when they do, they mutate the state safely without locks, or, if they use locks, then they do so in a way that guarantees that the global "lock order" is not violated. Strings are immutable data structures whose methods do not mutate anything, so string can be easily made safe. – Eric Lippert Mar 25 '12 at 16:15
  • "You can do so by using anonymous methods, lambdas, iterator blocks or async methods." That depends on your definition of local variable. While those are local variables as far as C# is concerned, they aren't not local variables as far as the CLR is concerned. – CodesInChaos Mar 26 '12 at 20:26
  • 3
    @CodeInChaos: Sure. But I prefer to not require people to understand implementation details in order to reason correctly about the language. The question is about local variables in C#, so it seems reasonable to take the C# definition of "local variable". Were the question about CIL, then it would get a different answer; that's a different language with different characteristics. – Eric Lippert Mar 26 '12 at 21:26
13

There is no hard and fast rule.

Here are some rules to make code thread safe in .NET and why these are not good rules:

  1. Function and all functions it calls must be pure (no side effects) and use local variables. Although this will make your code thread-safe, there is also very little amount of interesting things you can do with this restriction in .NET.
  2. Every function that operates on a common object must lock on a common thing. All locks must be done in same order. This will make the code thread safe, but it will be incredibly slow, and you might as well not use multiple threads.
  3. ...

There is no rule that makes the code thread safe, the only thing you can do is make sure that your code will work no matter how many times is it being actively executed, each thread can be interrupted at any point, with each thread being in its own state/location, and this for each function (static or otherwise) that is accessing common objects.

earlNameless
  • 2,878
  • 20
  • 25
  • 11
    Locks need not be slow. Locks are incredibly fast; an uncontested lock is in the order of magnitude of ten to a hundred **nanoseconds**. Contested locks are of course arbitrarily slow; if you are slowed down because you are contesting locks then *rearchitect the program to remove the contention*. – Eric Lippert Mar 24 '12 at 06:42
  • 2
    I guess I failed to make #2 clear enough. I was trying to say that there is a way to make threads safe: put locks around each access of any common objects. And assuming that there are multiple threads, it will generate contention, and locks will slow the whole thing down. When adding locks, one cannot just blindly add locks, they have to be placed in very good strategic places. – earlNameless Mar 24 '12 at 13:32
4

It must be synchronized, using an object lock, stateless, or immutable.

link: http://docs.oracle.com/javase/tutorial/essential/concurrency/immutable.html

kasavbere
  • 5,873
  • 14
  • 49
  • 72