traves thread subprocesos llamar hilos form example ejemplo controles como c# multithreading thread-safety

c# - subprocesos - Cómo hacer una clase Thread Safe



thread c# windows form (8)

De acuerdo con la respuesta de BCS:

BCS está describiendo el caso de un objeto sin estado. Un objeto de este tipo es intrínsecamente seguro para subprocesos porque no tiene variables propias que puedan ser obstruidas por llamadas de diferentes medios.

El registrador descrito tiene un identificador de archivo (lo siento, no es usuario de C #, tal vez se llame IDiskFileResource o algún MS-ism similar) que debe serializar el uso.

Entonces, separe el almacenamiento de mensajes de la lógica que los escribe en el archivo de registro. La lógica solo debe operar en un mensaje a la vez.

Una forma de hacerlo es: si el objeto del registrador mantuviera una cola de objetos de mensaje, y el objeto del registrador simplemente tenga la lógica de sacar un mensaje de la cola, luego extraiga las cosas útiles de un objeto de mensaje, luego escríbalo a el registro, luego busque otro mensaje en la cola - luego puede hacer que ese hilo sea seguro haciendo que el hilo de operaciones agregar / eliminar / queue_size / etc de la cola sea seguro. Requeriría la clase de registrador, una clase de mensaje y una cola segura de subprocesos (que probablemente sea una tercera clase, una instancia de la cual es una variable miembro de la clase de registrador).

Estoy escribiendo una aplicación de C #. Tengo (una especie de) clase de registro. y esta clase de registro será utilizada por muchos hilos. ¿Cómo hacer seguro este hilo de clase? ¿Debo hacerlo como singleton? ¿Cuáles son las mejores prácticas allí? ¿Hay algún documento que pueda leer acerca de cómo hacerlo seguro para subprocesos?

Gracias


En C #, cualquier objeto puede usarse para proteger una "sección crítica", o en otras palabras, un código que no debe ser ejecutado por dos hilos al mismo tiempo.

Por ejemplo, lo siguiente sincronizará el acceso al método SharedLogger.Write, por lo que solo un solo hilo está registrando un mensaje en un momento dado.

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


No estoy seguro de poder agregar algo a lo que ya se ha dicho acerca de cómo hacer que una clase de registro sea segura para subprocesos. Como se ha indicado, para hacer esto debe sincronizar el acceso al recurso, es decir, el archivo de registro, de modo que solo un hilo intente iniciar sesión a la vez. La palabra clave de lock C # es la forma correcta de hacer esto.

Sin embargo, abordaré (1) el enfoque de singleton y (2) la utilidad del enfoque que finalmente decida utilizar.

(1) Si su aplicación escribe todos sus mensajes de registro en un solo archivo de registro, entonces el patrón de singleton es definitivamente la ruta a seguir. El archivo de registro se abrirá al inicio y se cerrará al cerrar, y el patrón de singleton se ajusta perfectamente a este concepto de operaciones. Sin embargo, como lo señaló @dtb, recuerde que hacer de una clase un singleton no garantiza la seguridad de subprocesos. Utilice la palabra clave de lock para eso.

(2) En cuanto a la utilidad del enfoque, considere esta solución sugerida:

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

Permítanme primero decir que este enfoque generalmente está bien. Define una instancia singleton de SharedLogger través de la variable estática de la Instance e impide que otros ejemplifiquen la clase en virtud del constructor privado. Esta es la esencia del patrón de singleton, pero recomendaría encarecidamente leer y seguir los consejos de Jon Skeet con respecto a los singletons en C # antes de ir demasiado lejos.

Sin embargo, en lo que quiero centrarme es en la facilidad de uso de esta solución. Por ''usabilidad'', me refiero a la forma en que uno usaría esta implementación para registrar un mensaje. Considera cómo se ve la invocación:

SharedLogger.Instance.Write("log message");

Toda la parte de ''Instancia'' se ve mal, pero no hay forma de evitarla dada la implementación. En su lugar, considere esta 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); } } }

Observe que la clase ahora es estática, lo que significa que todos sus miembros y métodos deben ser estáticos. No es sustancialmente diferente del ejemplo anterior, pero considere su uso.

SharedLogger.Write("log message");

Eso es mucho más fácil de codificar contra.

El punto no es denigrar la solución anterior, sino sugerir que la facilidad de uso de la solución que elija es un aspecto importante que no debe pasarse por alto. Una buena API utilizable puede hacer que el código sea más fácil de escribir, más elegante y más fácil de mantener.


Si el rendimiento no es el problema principal, por ejemplo, si la clase no está muy cargada, simplemente haga esto:

Haga que su clase herede ContextBoundObject

Aplica este atributo a tu clase [Sincronización]

Toda tu clase ahora solo es accesible a un hilo a la vez.

Es realmente más útil para el diagnóstico, ya que la velocidad es casi el peor de los casos ... pero para determinar rápidamente "es un problema extraño una condición de carrera", póntelo, vuelva a ejecutar la prueba ... si el problema desaparece ... sabes que es un problema de hilos ...

Una opción más eficaz es hacer que su clase de registro tenga una cola de mensajes segura para subprocesos (que acepta mensajes de registro, luego simplemente sáquelos y proceselos secuencialmente ...

Por ejemplo, la clase ConcurrentQueue en el nuevo paralelismo es una buena cola segura para subprocesos.

O el usuario log4net RollingLogFileAppender, que ya es seguro para subprocesos .


Yo usaría un registrador listo para usar para esto, ya que hay varios que son sólidos y fáciles de usar. No hay necesidad de rodar el tuyo. Recomiendo Log4Net.


en mi opinión, el código provisto anteriormente ya no es seguro para subprocesos: en la solución anterior tuvo que instigar un nuevo objeto de SharedLogger y el método Write existió una vez para cada objeto.

Ahora solo tienes un método de escritura, que es usado por todos los hilos, ejemplo:

Tema 1: SharedLogger.Write ("Thread 1")

subproceso 2: SharedLogger.Write ("Thread 2");

public static void Write(string s) { // thread 1 is interrupted here <= lock (_lock) { _writer.Write(s); } }

  • El hilo 1 quiere escribir un mensaje, pero se interrumpe por el hilo 2 (ver comentario)
  • El hilo 2 anula el mensaje del hilo 1 y se interrumpe por el hilo 1

  • El subproceso 1 obtiene el bloqueo y escribe "Subproceso 2"

  • hilo 1 libera el bloqueo
  • El subproceso 2 obtiene el bloqueo y escribe "Subproceso 2"
  • hilo 2 libera el bloqueo

corrígeme cuando me equivoque ...


use lock() para que varios subprocesos no utilicen el registro al mismo tiempo


  • Pruebe y haga la mayoría de los cálculos utilizando variables locales y luego modifique el estado del objeto en un bloque de lock rápido.
  • Tenga en cuenta que algunas variables pueden cambiar entre cuando las lea y cuando altere el estado.