Static Doesn't Mean Thread Safe

One misunderstanding I often see is "if I make this member static it will be thread safe". I think this misconception arises because of the boilerplate MSDN documentation that will often say: "Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe."

When a .NET framework class appears with the above documentation, it means a developer took the necessary precautions to make the static member thread safe. Perhaps they added a lock, or used some other synchronization mechanism to ensure thread safety. See: Statics and Thread Safety Part I and Part II.

Marking a class member static doesn't guarantee thread safety. It is up to you to make the member thread safe.

There is one exception to this rule. It's a special case where the static / Shared keyword will guarantee threading magic. Do you know what that is?

posted on Wednesday, October 18, 2006 11:35 PM by scott

Comments

Wednesday, October 18, 2006 8:55 PM by Eric W. Bachtal

# re: Static Doesn't Mean Thread Safe

Are you talking about the guaranteed thread safety of static constructors and/or static member initializations?
Wednesday, October 18, 2006 9:19 PM by Barry Allison

# re: Static Doesn't Mean Thread Safe

static constructor?
Wednesday, October 18, 2006 9:52 PM by Geoff Appleby

# re: Static Doesn't Mean Thread Safe

I'll take a stab.
The shared constructor? It's guaranteed to run first, and to tun once, so it MUST be threadsafe, right? :)
Wednesday, October 18, 2006 11:18 PM by Cornelius van Berkel

# re: Static Doesn't Mean Thread Safe

I presume you mean the static constructor.

According to the specification the static constructor will only run once, and all concurrent acces to the type (static or instance) is hold up until the static constructor has finished.

Cheers
Wednesday, October 18, 2006 11:28 PM by JP

# re: Static Doesn't Mean Thread Safe

This could be the class constructor.
"the Common Language Infrastructure (CLI) spec that guarantees a type constructor will execute only once, unless explicitly invoked by user code. To enforce this guarantee in a multithreaded environment requires a lock to synchronize threads"
Do you think the following class should use locks to be thread safe?

public class Class1
{
private static IPEndPoint endPoint;
private static UdpClient listener;
private static int listenPort;

static Class1()
{
}

public static void ReceiveCallback(IAsyncResult ar)
{
listener.BeginReceive(new AsyncCallback(ReceiveCallback), s);
UdpClient u = (UdpClient)((UdpState)(ar.AsyncState)).U;
IPEndPoint e = (IPEndPoint)((UdpState)(ar.AsyncState)).E;
Byte[] receiveBytes = u.EndReceive(ar, ref e);
string receiveString = Encoding.ASCII.GetString(receiveBytes);

//Process string here
}

public static void Start(int port)
{
if (listener == null)
{
listenPort = port;
endPoint = new IPEndPoint(IPAddress.Loopback, listenPort);
listener = new UdpClient(endPoint);
UdpState s = new UdpState(endPoint, listener);
listener.BeginReceive(new AsyncCallback(ReceiveCallback), s);
}
}

public static void End()
{
if (listener != null)
listener.Close();
}
}
Thursday, October 19, 2006 1:22 AM by Keyvan Nayyeri

# re: Static Doesn't Mean Thread Safe

Good catch on this :-)

A few (one or two) times I got in trouble with that description which was in my mind and finally changed my logic to solve it.
Thursday, October 19, 2006 3:19 AM by jayson knight

# re: Static Doesn't Mean Thread Safe

static constructors?

I had this exact question during a recent interview.
Thursday, October 19, 2006 5:46 AM by scott

# re: Static Doesn't Mean Thread Safe

Good job, everyone. The runtime takes a lock beore calling a static / shared constructor to make sure it's only called once.

JP - If Thread A calls Class1.Start it will create a new UdpClient. If Thread B calls Class1.Start it will overwrite that client. When ThreadA calls End, it can close the listener opened by ThreadB, so I'd sat this class would have issues in a multi-threaded program.

Since the operations span methods you couldn't use a lock inside the class to manage this problem. Threads would need to take locks from Start() to End()
Thursday, October 19, 2006 8:28 AM by JP

# re: Static Doesn't Mean Thread Safe

Scott,
The Start method should not overwrite the listener because it checks that listener is null before doing anything.
Question about static events:
If that class has a static event and ASP.NET Page instances suscribe to it, it seems that the pages will not be garbage collected as the hold a reference to the event. Also suscriptions will keep adding up.
What is the best way to solve this? Unsuscribe the event in the Page unload, use Week References?
Thursday, October 19, 2006 8:57 AM by scott

# re: Static Doesn't Mean Thread Safe

JP:

- Think about two threads racing to get into Start at the same time. Thread A will check and see listener is null, then start creating a new endpoint. Thread B is there at the same time and also sees a null listener, and starts creating a new endpoint, too. There is nothing that prevents two threads from seeing a null listener.

- Subscribing to static events from a page is a real pain. You would have to unsubscribe at some point. A weak reference might work, too, but this is a bit tricky. See: http://www.interact-sw.co.uk/iangblog/2004/06/06/weakeventhandler.

I'd try to avoid the static event if at all possible.
Thursday, October 19, 2006 12:53 PM by jayson knight

# re: Static Doesn't Mean Thread Safe

A good discussion on static ctors is here: http://www.yoda.arachsys.com/csharp/beforefieldinit.html
Thursday, October 19, 2006 1:01 PM by protected virtual void jaysonBlog {

# Static Doesn't Mean Thread Safe

K. Scott Allen recently posted an interesting blurb on the thread safety-ness of static (Shared) members
Friday, October 20, 2006 4:29 AM by Speednet

# re: Static Doesn't Mean Thread Safe

I agree with this article, and I found the same thing.

Once I discovered this myself, that's when I figured out how to use the SyncLock command, which gets around thread safety.

You just have to be careful to keep the shared data locked for the shortest possible time, and be sure to include a Try/Catch for safety.