2012-01-04 12 views
27

Tengo un servidor que maneja múltiples conexiones de socket entrantes y crea 2 hilos diferentes que almacenan los datos en formato XML.¿Cuándo usar el hilo de bloqueo en C#?

Estaba usando la declaración lock para seguridad de hilos casi en todos los controladores de eventos llamados asincrónicamente y en los 2 hilos en diferentes partes del código. Lamentablemente, utilizando este enfoque mi aplicación se ralentiza significativamente.

He intentado no utilizar lock en absoluto y el servidor es muy rápido en la ejecución, incluso el almacenamiento de archivos parece aumentar; pero el programa se bloquea por razones que no entiendo después de 30 segundos - 1 minuto. de trabajo.

So. Pensé que la mejor manera es usar menos bloqueos o usarlo solo allí donde sea estrictamente necesario. Como tal, tengo 2 preguntas:

  1. es la cerradura necesaria cuando escribo a las variables públicas visitada (listas de C#) o incluso solamente cuando leí de ellos?

  2. ¿Es necesario el bloqueo solo en los subprocesos asíncronos creados por el manejador de socket o en otros lugares también?

Alguien podría darme algunas pautas prácticas, sobre cómo operar. No publicaré todo el código esta vez. No tiene sentido publicar alrededor de 2500 líneas de código.

+2

No es lo suficientemente específico, mira aquí para información general: http://www.albahari.com/threading/ – eulerfx

+1

Hay toda gama de posibles programas - de absolutamente equivocado/extraordinariamente rápido a siempre correcto/razonablemente lento. Parece que golpeas ambos extremos en tus experimentos. Enhebrar la seguridad no es un tema trivial, así que recomendaría leer sobre él en lugar de tratar de obtener "pautas prácticas". Es decir. El enlace de eulerfx es un buen punto de partida. –

Respuesta

33

¿Es necesario el bloqueo cuando escribo solo en las variables a las que accede el público (listas C#) o incluso cuando leo de ellas?

Sí (incluso cuando lo lea).

¿Es necesario el bloqueo solo en los hilos asíncronos creados por el manejador de socket o en otros lugares también?

Sí. Siempre que el código acceda a una sección de código que se comparte, siempre se bloquea.


esto suena como usted puede no estar bloqueando individuales objetos, pero el bloqueo una cosa para todos los situaciones de bloqueo.

Si es así puesto en inteligentes discretas cerraduras mediante la creación de objetos únicos individuales que relacionar y bloqueo sólo ciertas secciones a la vez, que no interfieren con otros hilos en otras secciones.

Aquí se muestra un ejemplo:

// This class simulates the use of two different thread safe resources and how to lock them 
// for thread safety but not block other threads getting different resources. 
public class SmartLocking 
{ 
    private string StrResource1 { get; set; } 
    private string StrResource2 { get; set; } 

    private object _Lock1 = new object(); 
    private object _Lock2 = new object(); 

    public void DoWorkOn1(string change) 
    { 
     lock (_Lock1) 
     { 
      _Resource1 = change; 
     } 
    } 

    public void DoWorkOn2(string change2) 
    { 
     lock (_Lock2) 
     { 
      _Resource2 = change2; 
     } 
    } 
} 
+3

Hey chico..Me salvaste la vida ...Usé cerraduras diferentes (con diferentes objetos estáticos) para diferentes situaciones y ahora la aplicación funciona como un amuleto. Muchas gracias. –

+0

¡Me sorprende que este hilo esté bloqueado! Sin juego de palabras. El uso incorrecto de cerraduras puede conducir a condiciones de carrera de hecho que me pareció que era el caso. Me alegro de que mi respuesta haya podido ayudarte. – OmegaMan

+10

@ClaudioFerraro: Eso es bueno, pero ahora puede estar cambiando un problema (mal rendimiento) por otro (puntos muertos). Puede entrar en una situación en la que el hilo 1 haya sacado el bloqueo A y esté esperando el bloqueo B, y el hilo 2 haya sacado el bloqueo B y esté esperando el bloqueo A, y por lo tanto, ambos esperarán por siempre. A medida que agrega un bloqueo más preciso a su programa **, debe establecer un protocolo de pedido estricto para todos los bloqueos **. Debe decir, por ejemplo, "Nunca permitiré que ningún hilo solicite el bloqueo 1 * después de * que ya haya obtenido el bloqueo 2". Tus problemas recién están comenzando; Esto es duro. –

0

Básicamente esto se puede responder bastante simple:

Es necesario bloquear todas las cosas que se accede por diferentes hilos. Realmente no importa si se trata de leer o escribir. Si está leyendo y otro hilo sobrescribe los datos al mismo tiempo, la lectura de datos puede no ser válida y es posible que esté realizando operaciones no válidas.

2

Utilice siempre un candado cuando tenga acceso a miembros (ya sea de lectura o escritura). Si está iterando sobre una colección, y desde otra cadena está eliminando elementos, las cosas pueden salir mal rápidamente.

Una sugerencia es cuando desea iterar una colección, copiar todos los elementos en una nueva colección y luego repetir la copia. Es decir.

var newcollection; // Initialize etc. 
lock(mycollection) 
{ 
    // Copy from mycollection to newcollection 
} 

foreach(var item in newcollection) 
{ 
    // Do stuff 
} 

Del mismo modo, solo use la cerradura en el momento en que esté escribiendo en la lista.

85

¿Alguna vez se ha sentado en su automóvil o en el autobús en una luz roja cuando no hay tráfico cruzado? Gran pérdida de tiempo, ¿verdad? Un candado es como un semáforo perfecto. Siempre es verde excepto cuando hay tráfico en la intersección.

Su pregunta es "Paso demasiado tiempo en el tráfico esperando las luces rojas. ¿Debo apagar la luz roja? O mejor aún, ¿debo quitar las luces por completo y dejar que todos conduzcan por la intersección a velocidades de autopista sin cualquier control de intersección? "

Si tiene un problema de rendimiento con los bloqueos, entonces quitar los bloqueos es el último que debe hacer. Usted está esperando en esa luz roja precisamente porque hay tráfico cruzado en la intersección. Los bloqueos son extraordinariamente rápidos si no se contestan.

No se puede eliminar la luz sin eliminar primero el tráfico cruzado. La mejor solución es por lo tanto a eliminar el tráfico cruzado. Si nunca se disputa el candado, nunca lo esperarás. Descubre por qué el tráfico cruzado está pasando tanto tiempo en la intersección; no quite la luz y espero que no haya colisiones. Habrá.

Si no puede hacer eso, entonces agregar más bloqueos de grano fino a veces ayuda a. Es decir, tal vez tengas todos los caminos de la ciudad convergiendo en la misma intersección. Quizás puedas dividir eso en dos intersecciones, de modo que el código se pueda mover a través de dos intersecciones diferentes al mismo tiempo.

Tenga en cuenta que hacer los coches más rápidos (conseguir un procesador más rápido) o hacer los caminos más cortos (eliminando código de longitud de recorrido) a menudo empeora el problema en escenarios de multiproceso. Tal como lo hace en la vida real; si el problema es un embotellamiento, comprar automóviles más rápidos y conducirlos en carreteras más cortas los lleva al atasco más rápido, pero no de manera más rápida.

+22

+1, siempre disfruto estas respuestas tipo metáfora. Hace las cosas mucho más fáciles de entender. – jadarnel27

+1

Gracias por la respuesta. Su respuesta fue bastante clara, pero mi intención era preguntar: "si en la vida real las personas inventaran círculos de tráfico, ¿cómo puedo determinar qué tan grandes deberían ser para tener un mejor rendimiento? Así que esperaba una respuesta más específica. Solo" el círculo debería ser aproximadamente 100 veces más grande que el anillo de mi esposa "o algo así! –

1

La razón por la que usted necesita para bloquear mientras que la lectura es:

digamos que usted está haciendo el cambio a una propiedad y que ha de ser leído dos veces mientras el hilo está entre medio de una cerradura. Una vez antes de hacer cualquier cambio y otro después, tendremos resultados inconsistentes.

Espero que ayude,