How does a locked singleton property keep threads safe?

I rarely use single dots, in which case this is appropriate. When I tried to investigate the best implementation, I came across this bit of code that made me believe that I misunderstood how brackets encapsulate a "region".

public sealed class Singleton { private static Singleton instance = null; private static readonly object padlock = new object(); Singleton() { } public static Singleton Instance { get { lock (padlock) { if (instance == null) { instance = new Singleton(); } return instance; } } } } 

I am confused what happens when I try to access the "Instance". Say I'm working on single-user logging (my useful app for singlet), and it has a method called “WriteLine (line string)”

When i call:

 Singleton.Instance.WriteLine("Hello!"); 

It supports locking during the execution of the entire WriteLine? Method.

What if I assign an instance to an external variable, for example:

 Singleton Console = Singleton.Instance; 

There is now a permanent link to a singleton outside of a singleton. Is Console.WriteLine("Hello!") Also completely thread safe, such as Singleton.Instance.WriteLine("Hello!") ?

In any case, I'm just confused how this makes a singleton thread safe and whether it is only thread safe when the property is explicitly available. I thought that Singlton.Instance.WriteLine("...") first pull out the instance, leaving the lock area, and then write WriteLine to the returned instance, so it would write after the lock was released.

Any help in resolving my misunderstanding of how these features will be rated.

+5
source share
3 answers

Does Singleton.Instance.WriteLine("Hello!"); support Singleton.Instance.WriteLine("Hello!"); lock during execution of the entire WriteLine method?

No, locking only protects the creation of your singleton. WriteLine unlocks (unless, of course, it gets its own lock inside).

Is Console.WriteLine("Hello!") Also completely thread safe, such as Singleton.Instance.WriteLine("Hello!") ?

Equally safe or unsafe as Singleton.Instance , since locking is not supported outside of Instance getter.

Anyway, I just got confused how this makes the single-user thread safe

Blocking makes the process of getting an instance of your single-threaded, thread-safe. Creating methods for your single-threaded, thread-safe process is a process that does not depend on whether your object is a singleton or not. There is no simple, simple “turnkey” solution designed to create an unsafe thread that will behave in a thread-safe manner. You access it one method at a time.

+5
source

No, the lock ends with return , all you do with Instance is "outside" the lock.

The advantage of lock at this point is only one:

  • It ensures that only one instance of Singleton can be created.

Note that in general, it is better to use the Lazy<> class. To get the same result, you will need to use it as:

 public static Lazy<Singleton> Instance = new Lazy<Singleton>(); 

( Lazy<T> can work in three modes by default, one, ExecutionAndPublication , is equivalent to this code)

+4
source

Any help in resolving my misunderstanding of how these features will be rated.

In Head First Design Patterns , there is a great example of a thread-safe singleton that uses “code magnets,” where you can look at all the possible ways that two threads can execute the same code. This is done with three columns, one for each of the two threads, and a third column for the value of the expected single item that is returned. This is an exercise in which you position the code fragments vertically to show the sequence of operations between two threads. I will try to reproduce it here with limited formatting in SO and with your sample code.

Code snippets (no lock):

  get { 
  if (instance == null) { 
  instance = 
             new Singleton ();  } 
  return instance;  } 

You may find one possible execution that leads to two instances of the returned class due to the way the threads execute:

  Thread One Thread Two Value instance 
  get {null 
  get {null 
  if (instance == null) {null 
  if (instance == null) {null 
  instance = 
             new Singleton ();  } Object_1 
  return instance;  } Object_1 
  instance = 
                                            new Singleton ();  } Object_2 
  return instance;  } Object_2 

With lock immediately after get { , Thread Two will not be able to continue (as indicated above) until Thread One executes a return instance; and will not release the lock:

  Thread One Thread Two Value instance 
  get {[takes lock] null 
  get {[blocks on lock] null 
  if (instance == null) {null 
  instance = 
             new Singleton ();  } Object_1 
  return instance;  } [releases lock] Object_1 
  [continues] 
                                  if (instance == null) {Object_1 
  return instance;  } Object_1 
+2
source

All Articles