2009-07-07 14 views
45

Ho una classe di timer statico che verrà chiamata da QUALSIASI pagina Web per calcolare per quanto tempo ciascuna pagina è stata presa per essere costruita.I metodi statici sono thread-safe

La mia domanda è sono le classi Static thread thread safe? Nel mio esempio utenti concorrenti causano un problema con i miei orari di inizio e fine? Ad esempio, un thread diverso sovrascrive i miei valori di avvio e arresto.

public static class Timer 
{ 
    private static DateTime _startTime; 
    private static DateTime _stopTime;  

    /// <summary> 
    /// Gets the amount of time taken in milliseconds 
    /// </summary> 
    /// <returns></returns> 
    public static decimal Duration() 
    { 
     TimeSpan duration = _stopTime - _startTime; 
     return duration.Milliseconds; 
    } 

    public static void Start() 
    { 
     _startTime = DateTime.Now; 
    } 

    public static void Stop() 
    { 
     _stopTime = DateTime.Now; 
    } 
} 

Questa classe dovrebbe essere una classe non statica?

(Questa classe sarà chiamato dal masterpage asp.net.)

+7

MSDN: "Mentre un'istanza di una classe contiene una copia separata di tutti i campi di istanza della classe, c'è solo una copia di ciascun campo statico." – colithium

risposta

56

I metodi statici non sono intrinsecamente thread-safe. Non vengono trattati in modo diverso dal CLR rispetto ai metodi di istanza. La differenza è che in genere si dovrebbe provare a rendere thread-safe. (Non riesco a pensare a metodi .NET BCL statici che non siano thread-safe.) I metodi di istanza spesso non sono thread-safe perché il pattern tipico è creare un oggetto e usarlo ripetutamente da un thread, e se fa deve essere utilizzato da più thread, la coordinazione coinvolta include assicurandosi che l'oggetto sia usato in modo sicuro. In molti casi è più appropriato farlo nel codice di coordinamento che nell'oggetto stesso. (Di solito vuoi rendere tutte le sequenze di operazioni effettivamente atomiche - qualcosa che non può essere fatto all'interno dell'oggetto.)

La tua classe Timer non è sicuramente thread-safe: due thread possono calpestare i reciproci dati con facilità e non c'è nulla che impedisca a un thread di utilizzare dati "obsoleti" quando si calcola la durata.

Utilizzare invece la classe Stopwatch - ecco a cosa serve. Certo, se si desidera utilizzare un'istanza da più thread, è necessario eseguire i normali passaggi per garantire la sicurezza, ma in generale si troverà in una posizione migliore. Certo, lo Stopwatch è tutt'altro che perfetto - vedi this question e il commento qui sotto per maggiori dettagli - ma è almeno per cosa è progettato il tipo. (Chissà, potrebbe essere risolto un po 'di tempo ...)

+4

La classe del cronometro ha dei problemi particolari, se la si utilizza con più core o più processori. Il cronometro usa un conteggio di zecche per determinare la durata del tempo e, a causa di un bug nel BIOS, il cronometro può essere avviato su un core e fermato su un altro, dove il tick conta sui due core non sincronizzati. L'ho scoperto nell'applicazione open source Vss2Git, che utilizzava un cronometro e talvolta tentava di fornire una durata temporale negativa. Per ulteriori informazioni consultare http://stackoverflow.com/a/7919483/216440 –

+1

@SimonTewsi: Sì, ne ho sentito parlare prima. Modifica un link nella risposta. –

4

Sì, hai ragione, gli statici soci/di accesso su questa classe causerà loro di essere sovrascritti da diversi utenti.

Ecco perché sono presenti istanze e membri non statici.

18

La tua classe di timer non è sicuramente thread-safe. Si dovrebbe creare una classe normale e un'istanza ogni volta che è necessario misurare il tempo:

Timer timer = new Timer(); 

timer.Start(); 
//... 
timer.Stop(); 

decimal duration = timer.Duration(); 

Meglio ancora, v'è un built-in classe .NET che fa esattamente questo:

Stopwatch sw = Stopwatch.StartNew(); 

sw.Stop(); 

TimeSpan duration = sw.Elapsed; 
20

Ci è una buona discussione here che si concentra maggiormente sui meccanismi e sui motivi per cui il tuo esempio non è thread-safe.

Per riepilogare, in primo luogo, le variabili statiche saranno condivise. Se poteste renderli variabili locali, anche se sono locali rispetto a un metodo statico, otterrebbero comunque il proprio stack frame e, quindi, sarebbero thread-safe. Inoltre, se si proteggono in altro modo le variabili statiche (ad es. Serrature e/o altre tecniche di programmazione multi-thread menzionate da altri in questo thread) si può anche rendere la classe statica protetta da thread.

In secondo luogo, poiché l'esempio non accetta istanze di variabili esterne modificate o il cui stato potrebbe essere interpretato da un altro thread, anche l'esempio è protetto da thread.

+0

Informazioni eccellenti, questo è esattamente quello che sono venuto a trovare qui: se un metodo statico utilizza solo variabili locali, è thread-safe? Saluti. –

+4

Sì. Ma ricorda, deve essere una variabile locale per il metodo, non una variabile di classe statica. (Penso che a volte le variabili di classe siano riferite come locali, ma potrei sbagliarmi.) Dato che hai detto "usa solo una variabile locale", suppongo anche che tu non stia assegnando un riferimento di un passato parametro su una variabile locale. – Bill