0

I know this topic has been pounded as I have read gillions of posts on SO as well as elsewhere about it, but I have yet to find a definitive answer, so my apologies if you are offended by this seeming redundancy.

I have a situation where there is a write-once, read-millions resource. The resource is extremely expensive to create and the contention on the write-lock could be very high as well. Furthermore, I cannot predict what processor this will be run on and so I do not know what the memory-model will be underneath. I am compiling 3 versions of the assembly built in .NET 3.5, .NET 4.0, and Mono 2.10.

Because of the high-contention for the resource, I need to make this as efficient as possible and would like to use a lock-free pattern, at least for the reads. I understand the double lock check pattern for creating the resource, but there has been disagreement over whether or not it works (on all processors) due to the access of _resource outside the memory barrier. Do I need to use volatile on the _resource field? Is a ReaderWriterLockSlim a better fit for this scenario? What other questions should I be asking?

if(_resource == null)
{
    lock(_locker)
    {
        if(_resource == null)
        {
            //create resource here...
        }      
    }
}

In addition, there will be a number of these:

if(_resource == null)
{
    return _resourceDefault;
}

return _resource.GetSomething();
Craig Wilson
  • 12,174
  • 3
  • 41
  • 45
  • Why even bother with locking here? – leppie Jul 16 '12 at 12:11
  • Because creating the resource is extremely expensive and I can't let it be done more than once. It includes remote communication with a server that tracks connections. – Craig Wilson Jul 16 '12 at 12:41
  • Well, get it over with early before threads start to read it. No lock required. This all is way too vague to give specific advice. – Hans Passant Jul 16 '12 at 13:58
  • I'm happy to clarify anything you need more information about. I tried to be specific. In regards to getting it over with, that is impossible due to some decisions made a while ago and current expectations of users. We may be able to change that in the future, but not at this time. – Craig Wilson Jul 16 '12 at 14:19
  • About the volatile, check out http://msdn.microsoft.com/en-us/library/ff650316.aspx under the multithreaded singleton header. The article cross references this, http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html which goes in depth on the topic. – Kenneth Ito Jul 16 '12 at 17:56

1 Answers1

1

Decided to promote my comment to an answer.

On creating singleton's read, http://msdn.microsoft.com/en-us/library/ff650316.aspx . It cross links a paper that goes in depth explaining some of the problems related to double checked locking. To be completely safe the microsoft article does recommend the volatile.

A reader writer lock doesn't really buy you anything if all you really just want is to create a singleton (one time thing... no lock promotion or anything is necessary. You really just need to protect creation of the resource). Once the resource is created you won't be touching the lock at all.

I would design access to your singleton to always go through a static property/method so you can always do the double check.

About the resource default, I don't think I know enough about your situation to answer properly. Do you expect the primary resource to ever be null (except before it was created)? Depending on your use case, you might be able to use a Moniter.TryEnter which is nonblocking and returns a value letting you know if you received the lock. If you were not able to immediately get the lock to create the singleton, you could return your default.

Kenneth Ito
  • 5,201
  • 2
  • 25
  • 44
  • Yes, there will be times when we can return some results without needing the resource. However, if the resource is there, we'll use it, otherwise, we'll just return some default. – Craig Wilson Jul 16 '12 at 18:17
  • I have read the paper you linked to, but there is a number of contradictory information related to when volatile should be used, when it shouldn't, etc... Normally, on x86 or x64 based machines, you don't need it. However, I can't be sure of what time of hardware I'll be running on and need to make sure an acquire-fence is used when reading. I guess I could just issue a Thread.MemoryBarrier(), but that starts to make things hard to read. Anyways, thanks for your effort, I'm going to wait and see if anyone else pipes in before accepting. – Craig Wilson Jul 16 '12 at 18:19
  • Seems volatile is not necessary on .NET 2.0 or greater, but since Mono doesn't offer the same guarantees, I've opted to be safe and use the volatile keyword. – Craig Wilson Jul 18 '12 at 14:09