2009-07-07 17 views
45

Tengo una clase de temporizador estático que CUALQUIER página web llamará para calcular cuánto tiempo ha tardado en construirse cada página.Son métodos estáticos de seguridad de subprocesos

Mi pregunta es ¿Las clases de Static son seguras? En mi ejemplo, ¿los usuarios simultáneos causarán un problema con mis horas de inicio y finalización? por ejemplo, hilos diferentes que sobrescriben mis valores de inicio y detención.

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; 
    } 
} 

¿Debería esta clase ser una clase no estática?

(Esta clase llamada desde el masterpage asp.net.)

+7

MSDN: "Mientras que una instancia de una clase contiene una copia separada de todos los campos de instancia de la clase, solo hay una copia de cada campo estático." – colithium

Respuesta

56

Los métodos estáticos no son inherentemente thread-safe. El CLR no los trata de manera diferente a los métodos de instancia. La diferencia es que generalmente se debe intentar hacer que sean seguros para subprocesos. (No puedo pensar en ningún método .NET BCL estático que no sea seguro para subprocesos.) Los métodos de instancias a menudo no son seguros para subprocesos porque el patrón típico es crear un objeto y usarlo repetidamente desde un subproceso, y si hace tiene que ser utilizado desde múltiples hilos, la coordinación involucrada incluye asegurarse de que el objeto se usa de forma segura. En muchos casos, es más apropiado hacerlo en el código de coordinación que en el objeto mismo. (Por lo general, desea hacer secuencias completas de operaciones atómicas de manera efectiva, algo que no se puede hacer dentro del objeto.)

Su clase Timer definitivamente no es segura para subprocesos: dos hilos pueden pisar los datos de los demás con facilidad , y no hay nada que impida que un hilo use datos "obsoletos" al calcular la duración.

En su lugar, usa la clase Stopwatch - eso es lo que está ahí. Es cierto que si desea utilizar una instancia de múltiples hilos, deberá seguir los pasos normales para garantizar la seguridad, pero en general se encontrará en una posición mucho mejor. Hay que admitir que Stopwatch está lejos de ser perfecto también - vea this question y el comentario a continuación para más detalles - pero es al menos para lo que está diseñado el tipo. (Quién sabe, se puede arreglar algún tiempo ...)

+4

La clase Cronómetro tiene problemas propios, si la está usando con múltiples núcleos o procesadores múltiples. Cronómetro utiliza un contador de ticks para determinar la duración del tiempo y, debido a un error en el BIOS, el Cronómetro puede iniciarse en un núcleo y detenerse en otro, donde el tictac cuenta en los dos núcleos no están sincronizados. Descubrí esto en la aplicación de código abierto Vss2Git, que utilizaba un cronómetro y en ocasiones intentaba dar una duración de tiempo negativa. Para más información, consulte http://stackoverflow.com/a/7919483/216440 –

+1

@SimonTewsi: Sí, he oído hablar de eso antes. Editará un enlace en la respuesta. –

4

Sí, tienes razón, los miembros/descriptores de acceso estáticos en esta clase hará que se pueden sobrescribir por diferentes usuarios.

Es por eso que tiene instancias y miembros no estáticos.

18

Su clase de temporizador definitivamente no es segura para subprocesos. Debe crear una clase normal y una instancia cada vez que se necesita para medir el tiempo:

Timer timer = new Timer(); 

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

decimal duration = timer.Duration(); 

Mejor aún, hay una clase incorporada .NET que hace exactamente eso:

Stopwatch sw = Stopwatch.StartNew(); 

sw.Stop(); 

TimeSpan duration = sw.Elapsed; 
20

Hay es una buena discusión here que se centra más en los mecanismos y las razones de por qué su ejemplo no es seguro para subprocesos.

Para resumir, primero, se compartirán sus variables estáticas. Si pudieras convertirlas en variables locales, aunque sean locales para un método estático, seguirían teniendo su propio marco de pila y, por lo tanto, serían seguros para la ejecución de subprocesos. Además, si protege sus variables estáticas (es decir, bloqueos y/u otras técnicas de programación de subprocesos múltiples mencionadas por otros en este hilo) también puede hacer que su muestra estética sea segura para subprocesos.

En segundo lugar, como su ejemplo no toma en instancias de variables externas que modifique o cuyo estado pueda ser afectado por otro subproceso, su ejemplo también es seguro para subprocesos.

+0

Excelente información, esto es exactamente lo que vine a encontrar aquí: Si un método estático solo usa variables locales, ¿es seguro para subprocesos? Aclamaciones. –

+4

Sí. Pero recuerde, debe ser una variable local del método, no una variable de clase estática. (Creo que a veces las variables de clase se conocen como locales, pero podría estar equivocado al respecto.) Como dijiste "solo usa una variable local", también tomo eso para implicar que no estás asignando una referencia de un pass-in parámetro a una variable local. – Bill

Cuestiones relacionadas