C# Interview questions

W

William Stacey [MVP]

My bad Bruce. The second thread will indeed create another instance as you
said. However, only the first will be used and the other will be GCd as you
guys said. The pattern still works as long as it does not matter if a
second instance is created and not used.

--
William Stacey [MVP]

| How very odd. That means that CompareExchange is not a method call,
| then. It's something else?
|
| If it's just a method like any other method, then according to the
| documentation at
|
| http://msdn2.microsoft.com/en-us/library/h7etff8w.aspx
|
| "The compare and exchange operations are performed as an atomic
| operation." Fair enough. That makes sense. However, the "new
| Singleton()" occurs _before_ the call to CompareExchange. That
| expression must be evaluated in order to make the call in the first
| place. Once inside the method, CompareExchange makes certain guarantees
| about the conditions under which the _instance variable will be set,
| but I see no such guarantee about the evaluation of method arguments
| before the method is invoked, nor do I understand how it would be
| possible for a method to make those sorts of guarantees.
|
| Then again, what I don't know about threading and locking would fill a
| thick book. :)
|
 
W

William Stacey [MVP]

If your singleton can never tolerate multiple instances, then here is
another one. This adds a lock and Interlocked.Exchange. The lock is only
here as a gate, not for the aquire/release memory barriers; the
Interlocked.Exchange is doing that for us. The Interlocked.Exchange does
two things. It makes sure instance is always read atomically as null or
!null and that all cpus will see the current value. And it insures
"instance" will be fully constructed and no cpus will ever see a partially
constructed instance (i.e. all instance var "stores" made in the
construction will be seen correctly). So this prevents the potential
problem of just doing "if(instance == null) instance = new Singleton();" If
I read it write, Vance Morrison [0] seems to claim that line would work
under 2.0, but Chris Brumme [1] seems to think it may not. So this is a
more conservative approach that should work in all cases. The lock and
Interlocked should only be hit once (or more in very rare cases) anyway, so
not really any cost in being conservative here.

public static Singleton Instance2
{
get
{
if (instance == null)
{
lock(syncRoot) // lock a static lock object.
{
if (instance == null)
Interlocked.Exchange<Singleton>(ref instance, new
Singleton());
}
}
return instance;
}
}

A side challenge is to come up with a method to Reset() instance to null to
force a refresh on next read in a safe way.

[0] - http://msdn.microsoft.com/msdnmag/issues/05/10/MemoryModels/
[1] - http://blogs.msdn.com/cbrumme/archive/2003/05/17/51445.aspx

--
William Stacey [MVP]

| How very odd. That means that CompareExchange is not a method call,
| then. It's something else?
|
| If it's just a method like any other method, then according to the
| documentation at
|
| http://msdn2.microsoft.com/en-us/library/h7etff8w.aspx
|
| "The compare and exchange operations are performed as an atomic
| operation." Fair enough. That makes sense. However, the "new
| Singleton()" occurs _before_ the call to CompareExchange. That
| expression must be evaluated in order to make the call in the first
| place. Once inside the method, CompareExchange makes certain guarantees
| about the conditions under which the _instance variable will be set,
| but I see no such guarantee about the evaluation of method arguments
| before the method is invoked, nor do I understand how it would be
| possible for a method to make those sorts of guarantees.
|
| Then again, what I don't know about threading and locking would fill a
| thick book. :)
|
 
L

Lucian Wischik

William Stacey said:
if (instance == null)
{ lock(syncRoot) // lock a static lock object.
{ if (instance == null)
{ Interlocked.Exchange<Singleton>(ref instance, new
Singleton());
}
}
return instance;

Then you don't need Interlocked.Exchange any more. You can do it just

if (instance==null)
{ lock(syncRoot)
{ if (instance==null) instance=new Singleton();
}
}
 
W

William Stacey [MVP]

| Then you don't need Interlocked.Exchange any more. You can do it just
|
| if (instance==null)
| { lock(syncRoot)
| { if (instance==null) instance=new Singleton();
| }
| }

That is where experts seems to disagree by my read. If you read the two
links in prev post, Vance seems to think that is ok, while Chris does not.
If Singleton constructor is empty, then probably yes. If not, instance
could be read as !null, but still not been fully constructed yet as viewed
by other cpus. Maybe it works under 2.0, maybe not. Maybe it will on mono,
maybe not. When experts disagree, I tend to side step the issue all
together and go with a more conservative approach that should work on all
memory models.

In fact, Vance says you don't even need the lock "at all" if your singleton
can tolerate multiple instances that will be GCd, but not used. This may be
true, but seems to throw out almost all of what I have learned and read on
the subject. Here is Vance's version:

public static LazyInitClass GetValue()
{
if (myValue == null)
myValue = new LazyInitClass();
return myValue;
}
 
W

William Stacey [MVP]

If we want ability to cycle our server singleton with a new version (for
some reason), we need a bit more logic to make it safe (I hope):

public static Singleton Instance3
{
get
{
Singleton tmp = instance;
if (tmp == null)
{
lock(syncRoot)
{
if (instance == null)
Interlocked.Exchange<Singleton>(ref instance, new
Singleton());
return instance;
}
}
return tmp;
}
}

internal static void Reset()
{
// Paired with Instance3.
SetInstance(null);
}

internal static void SetInstance(Singleton instance)
{
// Paired with Instance3.
// Set instance to a new instance for future Getters.
lock(syncRoot)
{
Interlocked.Exchange<Singleton>(ref instance, instance);
}
}

--
William Stacey [MVP]

| > if (instance == null)
| > { lock(syncRoot) // lock a static lock object.
| > { if (instance == null)
| > { Interlocked.Exchange<Singleton>(ref instance, new
| >Singleton());
| > }
| > }
| > return instance;
|
| Then you don't need Interlocked.Exchange any more. You can do it just
|
| if (instance==null)
| { lock(syncRoot)
| { if (instance==null) instance=new Singleton();
| }
| }
|
| --
| Lucian
 
S

Shark

Mythran said:
I find it interesting that you can modify that page without being logged in.

Mythran

yes everyone can edit the pages, but it seems that only interested
people visit the site, so things are under control. There are other
sections for Java, C++, C, etc. on the same site. I think its cool.
 
C

Chris Chilvers

If Singleton constructor is empty, then probably yes. If not, instance
could be read as !null, but still not been fully constructed yet as viewed
by other cpus.

Could this be solved by using a local variable and placing a memory barrior to force the constructor to have completed
before writing back to the shared variable?

if (instance==null) {
lock(syncRoot) {
if (instance==null) {
Singleton temp = new Singleton();
Thread.MemoryBarrier();
instance=temp;
}
}
}

Would this be correct? As this is my current understanding of what a memory barrier is supposed to do. And am I right in
thinking this is only safe when instance is null, if we were reloading the singleton for whatever reason it would be
possible for other caches to have a stale pointer for instance cached dispite the memory barrier without marking insance
as volitile?
 
W

William Stacey [MVP]

Yes it can be fixed with memory barrier(s). But I am not sure if that
location is correct or not. You need a read barrier, but not sure if that
does it. Interlocked.Exchange() does both so you don't have to guess.

--
William Stacey [MVP]

| On Fri, 28 Apr 2006 15:25:56 -0400, "William Stacey [MVP]"
|
| >If Singleton constructor is empty, then probably yes. If not, instance
| >could be read as !null, but still not been fully constructed yet as
viewed
| >by other cpus.
|
| Could this be solved by using a local variable and placing a memory
barrior to force the constructor to have completed
| before writing back to the shared variable?
|
| if (instance==null) {
| lock(syncRoot) {
| if (instance==null) {
| Singleton temp = new Singleton();
| Thread.MemoryBarrier();
| instance=temp;
| }
| }
| }
|
| Would this be correct? As this is my current understanding of what a
memory barrier is supposed to do. And am I right in
| thinking this is only safe when instance is null, if we were reloading the
singleton for whatever reason it would be
| possible for other caches to have a stale pointer for instance cached
dispite the memory barrier without marking insance
| as volitile?
 
C

Chris Chilvers

Yes it can be fixed with memory barrier(s). But I am not sure if that
location is correct or not. You need a read barrier, but not sure if that
does it. Interlocked.Exchange() does both so you don't have to guess.

From what I've read does Monitor.Enter not have a read barrier and Monitor.Exit have a write barrier? Unlike the
previous language I used, java which only has a read barrier making double checked locking impossible.
 
W

William Stacey [MVP]

yes. Monitor does. But I think the issue is the first "if
(instance==null)" test. A second thread could see instance != null before
the first thread exits the lock with a write barrier. Moreover, the test
does not have a read barrier.
I did not know that about java. Is that still true today?

--
William Stacey [MVP]

| On Fri, 28 Apr 2006 22:52:17 -0400, "William Stacey [MVP]"
|
| >Yes it can be fixed with memory barrier(s). But I am not sure if that
| >location is correct or not. You need a read barrier, but not sure if
that
| >does it. Interlocked.Exchange() does both so you don't have to guess.
|
| From what I've read does Monitor.Enter not have a read barrier and
Monitor.Exit have a write barrier? Unlike the
| previous language I used, java which only has a read barrier making double
checked locking impossible.
 
C

Chris Chilvers

yes. Monitor does. But I think the issue is the first "if
(instance==null)" test. A second thread could see instance != null before
the first thread exits the lock with a write barrier. Moreover, the test
does not have a read barrier.

That's why I placed the memory barrier between creating it and assigning it to the class member:
Singleton temp = new Singleton();
Thread.MemoryBarrier();
instance=temp;

I suppose this still could leave the ability for another thread to receive 'half' a pointer if the pointer was larger
than a standard data size making the write non-atomic. I was just wondering about this as this was how I implemented
this in the past, didn't know about those interlock classes as I've only began looking at multithreading in .net
recently. One of the thinks I couldn't work out was when playing with ContextBound objects and syncronization contexts
when I was looking at the third parameter to 'bool Monitor.Wait(object, int, bool)'. Got a post entiled
I did not know that about java. Is that still true today?
Not sure, might have been update, it's been a while since I used it (jre 1.4.3, they are on 1.5.something now at least).
 
J

Jon Skeet [C# MVP]

Chris Chilvers said:
From what I've read does Monitor.Enter not have a read barrier and
Monitor.Exit have a write barrier? Unlike the previous language I
used, java which only has a read barrier making double checked
locking impossible.

No, Java has both read and write barriers, but of a different sort -
they only refer to a single variable at a time, rather than the whole
memory space, IIRC. That's what makes double-checked locking impossible
- it's possible for the singleton reference variable to be written and
available to other threads before all the variables within the
singleton have been made available.
 
J

Jon Skeet [C# MVP]

I suppose this still could leave the ability for another thread to
receive 'half' a pointer if the pointer was larger than a standard
data size making the write non-atomic.

Reference reads and writes are guaranteed to be atomic (but not
necessarily volatile).

Using a volatile variable for the singleton reference makes double-
checked locking valid in .NET, I believe - but frankly I prefer simpler
ways which rely on static initialisers instead. Much simpler.

(Btw - if you could change your newsreader to wrap at 72 characters, it
would make it easier to reply to your posts.)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top