Static Doesn't Mean Thread Safe

Thursday, October 19, 2006

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?


Comments
Eric W. Bachtal Thursday, October 19, 2006
Are you talking about the guaranteed thread safety of static constructors and/or static member initializations?
Barry Allison Thursday, October 19, 2006
static constructor?
Geoff Appleby Thursday, October 19, 2006
I'll take a stab.
The shared constructor? It's guaranteed to run first, and to tun once, so it MUST be threadsafe, right? :)
Cornelius van Berkel Thursday, October 19, 2006
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
JP Thursday, October 19, 2006
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();
}
}
Keyvan Nayyeri Thursday, October 19, 2006
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.
jayson knight Thursday, October 19, 2006
static constructors?

I had this exact question during a recent interview.
scott Thursday, October 19, 2006
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()
JP Thursday, October 19, 2006
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?
scott Thursday, October 19, 2006
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: www.interact-sw.co.uk/.../weakeventhandler.

I'd try to avoid the static event if at all possible.
jayson knight Thursday, October 19, 2006
A good discussion on static ctors is here: www.yoda.arachsys.com/csharp/beforefieldinit.html
Speednet Friday, October 20, 2006
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.
Comments are now closed.
by K. Scott Allen K.Scott Allen
My Pluralsight Courses
The Podcast!