views:

176

answers:

3

I have a class with code as follows

private readonly object m_lock = new object();

private IClient m_client
private object m_context;

When setting the client and context, I lock as follows

lock(m_lock)
{
    m_client = theClientFromSomewhere;
    m_context = contextObject;
}

My question is, if I only need to get the m_client by itself, is it safe to do this?

var localClient = m_client;

Debug.Assert(localClient != null);
localClient.DoStuff();

m_client is a reference type, so the read (when assigning to localClient) is guaranteed to be atomic, so this should work fine on a single CPU.

I could (also in theory) make the m_client variable volatile, and then this would be safe across multiple cpu's by preventing out-of-order reads by other CPU's, but the question is, does the lock-when-writing make it safe to read without being volatile?

Does locking when writing "flush" the CPU caches so that when they do a read it won't be out-of-order?

+3  A: 

lock in C# (and, in general, the Monitor that it expands to in .NET) is a memory barrier - specifically, read barrier on acquire, write barrier on release. As for volatile, it adds a barrier for every read and write to the field. So, yes, you should be safe with volatile (assuming the rest of your code, which you didn't show, is doing everything correctly).

Pavel Minaev
I'm aware that volatile would be safe across multiple CPU's, but is this safe without making it volatile (when I only want to read m_client and not the context)?
Orion Edwards
Hrm... On further thought it won't be safe, as I'd need a read barrier before the localClient = m_client... Can anyone confirm if this is correct or not?
Orion Edwards
Yes, this is precisely correct. If you don't have a read barrier there (either explicit, or `volatile`, or whatever else it may come from), you may read a stale value.
Pavel Minaev
A: 

If you didn't have m_context, you wouldn't need the lock as both the read and the write are atomic. If, however, when you read m_client you also use m_context, then you have to lock both to prevent a situation where there's a context switch after updating m_client but before updating m_context.

Tal Pressman
A: 

If I recall, the performance is not great, but you could use a ReaderWriterLock to implement 1 writer and multiple readers.

SnOrfus
If you're going to use that, use `ReadWriterLockSlim` rather than `ReadWriterLock`.
Pavel Minaev