2013-03-05 14 views
10

Sto lavorando su un'applicazione C# multithread che sta consumando un servizio Web WCF. La connessione al webservice avrà un timeout specifico che possiamo definire e dopo il quale verrà chiuso. Sto cercando di memorizzare la connessione al servizio web utilizzando la classe singleton. Sto cercando di ottenere l'istanza come segue:Singleton pigro in un'applicazione C# multithreaded

CLazySingleton ins = CLazySingleton.Instance; 
string connection = CLazySingleton.abc; 

Di seguito si riporta il codice per la classe Singleton:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace LazySingleton 
{ 
    public class CLazySingleton 
    { 
     private static readonly Lazy<CLazySingleton> _instance 
      = new Lazy<CLazySingleton>(() => new CLazySingleton()); 
     private static readonly object ThreadLock = new object(); 
     public static string abc; 
     //I will use the service connection object in place of 'abc' in the application 
     //assume that 'abc' is storing the connection object  

     private CLazySingleton() 
     { } 

     public static CLazySingleton Instance 
     { 
      get 
      { 
       if (abc == null) 
       { 
        lock (ThreadLock) 
        { 
         //Make the connection 
         abc = "Connection stored in this variable"; 
         Console.WriteLine("Connection Made successfully"); 
         return _instance.Value; 
        }      
       } 
       else 
       { 
        return _instance.Value; 
       } 
      } 
     } 
    } 
} 

Le mie domande sono: 1. Sarebbe questo codice in grado di prendersi cura di più thread che cercano di ottenere l'istanza allo stesso tempo? Questa è attualmente la mia più grande preoccupazione. 2. Posso avere una soluzione migliore per questo? 3. Devo usare il "blocco" qui o l'approccio Lazy si occupa dei multithread che cercano di ottenere l'istanza?

Qualsiasi aiuto sarebbe apprezzato.

Grazie!

+1

Dai un'occhiata a questo articolo di Jon Skeet. Fa un buon lavoro nel discutere il modello Singleton. http://www.yoda.arachsys.com/csharp/singleton.html – juharr

+0

Ho qualche dubbio sul tuo approccio. Perché è necessario mantenere un Singleton per la connessione? C'è un problema se ogni thread ha il proprio proxy/connessione? E poiché si tratta di un servizio web, non prevedo alcun problema se crei molte connessioni. --- Per capire meglio qual è il tipo di oggetto che è la tua 'connessione'? – thewpfguy

+0

non è necessario il blocco – Rafa

risposta

5

uso semplice ThreadSafetyMode

Lazy<MergeSort> ty = new Lazy<MergeSort>(LazyThreadSafetyMode.ExecutionAndPublication); 
+6

Il costruttore di default di Lazy utilizza LazyThreadSafetyMode.ExecutionAndPublication, per impostazione predefinita è thread-safe. – Phil

+0

Grazie, ci proverò sicuramente. –

8

Secondo la documentazione di Microsoft Lazy Initialization, nella sezione intitolata "thread-safe inizializzazione":

Per impostazione predefinita, Lazy oggetti sono thread-safe.

Con questo in mente, il vostro campo abc non deve essere statica. Poiché stai utilizzando un Lazy<T> per istanziare il tuo singleton, è possibile inizializzare la connessione in modo sicuro nel costruttore CLazySingleton.

+0

Ciao Richard, grazie per la risposta. Hai fatto un buon punto. Anche io stavo riflettendo sullo stesso. Ma la mia domanda è, se scrivo la logica per fare la connessione nel costruttore, come gestirò lo scenario in cui la connessione viene espirata da sola dopo il timeout specificato. In tal caso, il campo abc diventerà nullo e la classe singleton continuerà a restituirlo come null solo in quanto il costruttore non verrà richiamato. Spero di poter esprimere il mio punto. –

+0

Penso che l'uso di Singleton non sia lo schema giusto qui, in realtà non è necessario gestire la connessione in questo modo. E sei sicuro di aver chiamato un servizio web, perché qual è il tipo di timeout che menzioni qui? – thewpfguy

3

Questo codice è in grado di occuparsi di più thread cercando di ottenere l'istanza allo stesso tempo?

Nel tuo scenario può essere il campo "abc" inizializzato due volte. Immagina la situazione, che la variabile "abc" è nulla. Il primo thread sarà all'interno del blocco "lock" prima dell'assegnazione del valore. Il secondo thread sarà in attesa prima del blocco. Quindi il primo thread inizializzerà "abc" e il secondo thread lo reinizializzerà (il tuo controllo per null è al di fuori del blocco, questa è la ragione). Ma forse questo non è qualcosa di cui dovresti aver paura.

Posso avere una soluzione migliore per questo?

Sì, è possibile. Lasciatemi descriverlo nell'ultimo blocco di questa risposta.

Devo utilizzare il "blocco" qui o l'approccio Lazy si occupa dei multithread che cercano di ottenere l'istanza?

La creazione della proprietà Value nella classe Lazy è thread-safe. Nel tuo scenario utilizzerei il vantaggio della classe IsValueCreated di Lazy <>. Sarà comunque necessario l'oggetto ThreadLock.Solo una cosa è, che una volta che si accede proprietà Value di Lazy <> classe, proprietà IsValueCreated restituirà vero (che è il trucco ;-))

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace LazySingleton 
{ 
    public class CLazySingleton 
    { 
     private static readonly Lazy<CLazySingleton> _instance 
      = new Lazy<CLazySingleton>(() => new CLazySingleton()); 
     private static readonly object ThreadLock = new object(); 
     public static string abc; 
     //I will use the service connection object in place of 'abc' in the application 
     //assume that 'abc' is storing the connection object  

     private CLazySingleton() 
     { } 

     public static CLazySingleton Instance 
     { 
      get 
      { 
       if (_instance.IsValueCreated) 
       { 
        return _instance.Value; 
       } 
       lock (ThreadLock) 
       { 
        if (abc == null) 
        { 
         abc = "Connection stored in this variable"; 
         Console.WriteLine("Connection Made successfully"); 
        } 
       } 
       return _instance.Value; 
      } 
     } 
    } 
}