miguel
miguel

Reputation: 3009

Thread Local Storage and local method variables

In c#, each thread has its own stack space.

If this is the case, why is the following code not thread-safe? (It is stated that this code is thread-safe on this post: Locking in C#

class Foo 
{ 
    private int count = 0; 
    public void TrySomething()     
    { 
        count++; 
    } 
} 

As count is an int (stack variable), surely this value would be isolated to an individual thread, on its own stack, and therefore thread-safe?

I am probably missing something here, but I dont understand what is actually in Thread Local Storage if not stack-based variables for the thread?

Also, what about locally-declared variables:

class Foo 
{ 
    public void TrySomething(object myObj)     
    { 
       var localVariable = new object();
       localVariable = myObj;
    } 
} 

What are the implications here on the local variable? Is it still heap-based? Is it thread-safe?

Upvotes: 2

Views: 2081

Answers (3)

John Källén
John Källén

Reputation: 7943

But count isn't a stack variable; it's a member variable of a class. If you pass a reference to an instance of Foo to another thread, you are in a non-thread-safe situation.

Thread Local Storage (TLS) is a wholly different concept than member variables. TLS associates values with the thread and not with a specific class. Think of it as global variables (or static member variables of classes), except that they are scoped to only be visible within a single thread; other threads can't even see then. They are not stored in the thread's stack, but in a special area that is private to the thread.

There still is room for trouble, though. If you store a reference to a Foo in TLS in a thread it's initially invisible to other threads. If you then somehow copy that reference into another thread, you again are in a thread-unsafe situation, regardless of the fact that the first reference lives in TLS.

Upvotes: 2

Simon P Stevens
Simon P Stevens

Reputation: 27499

Count is a member variable of the class Foo. As Foo is a reference type it is stored on the heap not the stack.

If you created only 1 Foo object and allowed two separate threads to call the TrySomething() method, they would both be calling the method on the same object (stored on the heap), and both would attempt to increment the same member as it's part of that same object. (See Darin's sample code)

The difference between structs and classes is not whether they are stored on the heap or the stack. This is a common misconception. Think of classes as pointers to storage on the heap. Think of structs as direct values being stored locally. At a very simple level, this does mean that structs are often stored on the stack, it's certainly not true in all cases as this example shows.

Upvotes: 3

Darin Dimitrov
Darin Dimitrov

Reputation: 1038790

This code is not thread safe because two threads can execute this method on the same instance of Foo, meaning that the count variable will be the same:

var foo = new Foo();
new Thread(foo.TrySomething).Start();
new Thread(foo.TrySomething).Start();

The TrySomething method will be executed concurrently on two different threads using the same count variable, so every access to this count variable has to be synchronized:

public void TrySomething()     
{ 
    Interlocked.Increment(ref count);
} 

Upvotes: 2

Related Questions