2009-05-28 11 views
6

En una aplicación de C#, supongamos que tengo una sola clase mundial que contiene algunos elementos de configuración, así:C de seguridad # hilo de parámetros de configuración globales

public class Options 
{ 
    int myConfigInt; 
    string myConfigString; 
    ..etc. 
} 

static Options GlobalOptions; 

los miembros de esta clase será usos a través de diferentes temas:

Thread1: GlobalOptions.myConfigString = blah; 

mientras

Thread2: string thingie = GlobalOptions.myConfigString; 

Usando un bloqueo para el acceso a los GlobalOptions objeto también sería innecesario bloquear cuando 2 hilos están accediendo a miembros diferentes, pero por otro lado la creación de un objeto de sincronización para cada miembro también parece un poco exagerado.

Además, usar un bloqueo en las opciones globales haría que mi código fuera menos agradable, creo; si tengo que escribir

string stringiwanttouse; 
lock(GlobalOptions) 
{ 
    stringiwanttouse = GlobalOptions.myConfigString; 
} 

todas partes (y es este thread-safe o es stringiwanttouse ahora sólo un puntero a myConfigString? Sí, soy nuevo en C# ....) en lugar de

string stringiwanttouse = GlobalOptions.myConfigString; 

hace que el código se vea horrible.

Así que ... ¿Cuál es la mejor (y la más simple) forma de garantizar la seguridad de las hebras?

+0

Creo que sería más claro aún si pudiera especificar qué problema está tratando de resolver. No hay peligro inherente en dos hilos que establecen un valor global más o menos al mismo tiempo. El CLR no se bloqueará. ¿Qué tipo de problema de sincronización estás tratando de resolver? – PeterAllenWebb

+0

Bueno, ¿qué sucede cuando un hilo está escribiendo en una cadena mientras que otro está tratando de leerlo? ¿Obtendré siempre una cadena u otra, o podría suceder que recibo una cadena sobrescrita parcialmente? – Led

Respuesta

4

Puede envolver el campo en cuestión (myConfigString en este caso) en una propiedad y tener un código en el Get/Set que utiliza un bloqueo de monitor o un Mutex. Luego, acceder a la propiedad solo bloquea ese campo individual y no bloquea toda la clase.

Editar: añadir código

private static object obj = new object(); // only used for locking 
public static string MyConfigString { 
    get { 
     lock(obj) 
     { 
      return myConfigstring; 
     } 
    } 
    set { 
     lock(obj) 
     { 
      myConfigstring = value; 
     } 
    } 
} 
+0

+1 Además, no es necesario bloquear entradas ni ningún objeto que garantice operaciones atómicas (es decir, seguridad de la secuencia). –

+0

buena captura! thx Ed. – Mike

+0

Ed que es una declaración peligrosa. http://thith.blogspot.com/2005/11/c-interlocked.html – dss539

0

Sus configuraciones pueden ser 'global', pero deben no ser expuesto como una variable global. Si las configuraciones no cambian, se deben usar para construir los objetos que necesitan la información, ya sea manualmente o a través de un objeto de fábrica. Si cambian , se debe usar un objeto que observe el archivo de configuración/base de datos/lo que sea e implemente el Observer pattern.

Las variables globales (incluso aquellos que resultan ser una instancia de clase) son una mala cosa ™

+0

en este caso se trata principalmente de una cadena que especifica un directorio de descarga que el usuario puede cambiar en cualquier momento. Creo que el patrón Observer podría ser excesivo para solo 1 o 2 cadenas globales. – Led

+0

Para elaborar, esto no es código de producción. Conozco la ética de la programación;) Esta es solo una pequeña herramienta de proyecto hobby para mi propio uso. – Led

3

Lo siguiente fue escrito antes de edición de la OP:

public static class Options 
{ 
    private static int _myConfigInt; 
    private static string _myConfigString; 

    private static bool _initialized = false; 
    private static object _locker = new object(); 

    private static void InitializeIfNeeded() 
    { 
     if (!_initialized) { 
      lock (_locker) { 
       if (!_initialized) { 
        ReadConfiguration(); 
        _initalized = true; 
       } 
      } 
     } 
    } 

    private static void ReadConfiguration() { // ... } 

    public static int MyConfigInt { 
     get { 
      InitializeIfNeeded(); 
      return _myConfigInt; 
     } 
    } 

    public static string MyConfigString { 
     get { 
      InitializeIfNeeded(); 
      return _myConfigstring; 
     } 
    } 
    //..etc. 
} 

Después de eso edición , Puedo decir que debe hacer algo como lo anterior, y solo configurar la configuración en un solo lugar: la clase de configuración. De esta forma, será la única clase que modificará la configuración en tiempo de ejecución, y solo cuando se recupere una opción de configuración.

+0

No creo que esté tratando de sincronizar el acceso a su archivo de configuración, que es lo que parece que ha dado. Además, ha proporcionado acceso de solo lectura. – dss539

+0

Deliberadamente hice el acceso de solo lectura. Es una clase de configuración. Lo único que modifica la configuración debe ser la clase. –

+0

Mi error, perdón John, después de volver a leer mi pregunta, me pareció demasiado vaga, así que traté de elaborar un poco ... – Led

0

¿Qué quieres decir con seguridad de hilo aquí? No es el objeto global el que necesita ser seguro para subprocesos, es el código de acceso. Si dos hilos escriben en una variable miembro cerca del mismo instante, uno de ellos "ganará", pero ¿es eso un problema?Si el código de su cliente depende del valor global que se mantiene constante hasta que se completa con alguna unidad de procesamiento, deberá crear un objeto de sincronización para cada propiedad que deba bloquearse. No hay una gran manera de evitar eso. Puede guardar en caché una copia local del valor para evitar problemas, pero la aplicabilidad de esa solución dependerá de sus circunstancias. Además, no crearía un objeto de sincronización para cada propiedad de manera predeterminada, sino que, en su lugar, te darás cuenta de que lo necesitarás.