Non sono sicuro di poter aggiungere nulla a ciò che è già stato detto su come rendere sicura una classe di log. Per fare ciò è necessario sincronizzare l'accesso alla risorsa, ad esempio il file di registro, in modo che solo un thread tenti di collegarsi ad esso alla volta. La parola chiave C# lock
è il modo corretto per farlo.
Tuttavia, affronterò (1) l'approccio singleton e (2) l'usabilità dell'approccio che alla fine deciderete di utilizzare.
(1) Se l'applicazione scrive tutti i suoi messaggi di registro in un singolo file di registro, il pattern singleton è sicuramente la strada da percorrere. Il file di registro verrà aperto all'avvio e chiuso all'arresto e il modello singleton si adatta perfettamente a questo concetto di operazioni. Come ha sottolineato @dtb, però, ricorda che fare una classe a singleton non garantisce la sicurezza del thread. Utilizzare la parola chiave lock
per quello.
(2) Per quanto riguarda l'usabilità del metodo, si consideri questa soluzione suggerita:
public class SharedLogger : ILogger
{
public static SharedLogger Instance = new SharedLogger();
public void Write(string s)
{
lock (_lock)
{
_writer.Write(s);
}
}
private SharedLogger()
{
_writer = new LogWriter();
}
private object _lock;
private LogWriter _writer;
}
Permettetemi di dire che questo approccio è generalmente male. Definisce un'istanza singleton di SharedLogger
tramite la variabile statica Instance
e impedisce ad altri di creare un'istanza della classe in virtù del costruttore privato. Questa è l'essenza del modello singleton, ma consiglio vivamente di leggere e seguire il consiglio di Jon Skeet riguardante singletons in C# prima di andare troppo lontano.
Tuttavia, quello su cui voglio concentrarmi è l'usabilità di questa soluzione. Per "usabilità", mi riferisco al modo in cui si userebbe questa implementazione per registrare un messaggio. Considerate ciò che l'invocazione assomiglia:
SharedLogger.Instance.Write("log message");
che tutta la parte 'grado' sembra solo sbagliato, ma non c'è modo di evitarlo dato l'implementazione. Invece, di questa alternativa:
public static class SharedLogger : ILogger
{
private static LogWriter _writer = new LogWriter();
private static object _lock = new object();
public static void Write(string s)
{
lock (_lock)
{
_writer.Write(s);
}
}
}
Si noti che la classe è ormai statico, il che significa che tutti i suoi membri e metodi devono essere statico. Non è sostanzialmente diverso dall'esempio precedente, ma considera il suo utilizzo.
SharedLogger.Write("log message");
Questo è molto più semplice da codificare.
Il punto non è denigrare la soluzione precedente, ma suggerire che l'usabilità di qualsiasi soluzione tu scelga è un aspetto importante da non trascurare. Una buona API utilizzabile può rendere il codice più semplice da scrivere, più elegante e più facile da mantenere.
Il modello singleton non implica la sicurezza del thread. – dtb
Inizia definendo accuratamente cosa intendi per "thread safe". Le persone usano questo termine come se significasse qualcosa di specifico, quando in realtà significa semplicemente "funziona correttamente nello scenario X". Senza una specifica per "correttamente" e una dichiarazione su cosa sia X, non si può effettivamente implementare qualcosa e si sa che hai risolto un problema che davvero hai. –
Dai un'occhiata a [questo grande articolo] (http://www.albahari.com/threading/part2.aspx#_ThreadSafety) di Joseph Albahari. Circa a metà dell'articolo è una grande sezione su "Thread Safety" –