2012-04-01 10 views
5

Suponiendo Tengo un objeto A que contieneAlcance de cerradura en C#: ¿el objeto devuelto sigue "bloqueado"?

// ... 

private List<double> someList = new List<double>(); 

// ... 

public List<double> SomeList 
{ 
    get { lock (this) { return someList; } } 
} 

// ... 

sería hilo de seguridad para realizar la operación en la lista como en el código siguiente. Sabiendo que varias operaciones podrían ejecutarse simultáneamente por diferentes hilos.

A.SomeList.Add(2.0); 

o

A.SomeList.RemoveAt(0); 

En otras palabras, cuando se libera el bloqueo?

+5

Nunca debe bloquear un objeto expuesto fuera de su clase, como 'this'. – svick

+0

Ok, es suficiente. Suponiendo que bloqueo un objeto no expuesto afuera. –

+0

@svick ¿por qué no? – surfen

Respuesta

5

El bloqueo que se muestra en la pregunta no es de mucha utilidad.

Para hacer que las operaciones de la lista sean seguras, debe implementar sus propios métodos Agregar/Quitar/etc para ajustar los de la lista.

public void Add(double item) 
{ 
    lock(_list) 
    { 
     _list.Add(item); 
    } 
} 

Además, es una buena idea esconder la lista de los consumidores de su clase, es decir, hacer el campo privado.

+0

Gracias, eso era lo que temía. –

+2

No se puede acceder a 'SyncRoot' en una' Lista 'así porque es una implementación de interfaz explícita. Y creo que no hay una buena razón para usarlo, es más que nada por compatibilidad con versiones anteriores. – svick

+0

@svick: Gracias. Corregido – Gebb

12

No hay seguridad de subprocesos aquí.

El lock se libera tan pronto como el bloque que protege termina, justo antes de que la propiedad regrese, por lo que las llamadas a Add ad RemoveAt no están protegidas por el bloqueo.

+0

Gracias por lo que pensé. ¿Hay alguna manera común de hacer que una situación como esa sea segura? –

+0

@ user1162647 - Sí, bloquea las _operaciones_ en el objeto compartido que desea proteger. – Oded

2

El bloqueo se libera cuando el código dentro del bloqueo se termina de ejecutar. Además, bloquear esto solo afectará a la instancia actual del objeto

3

El bloqueo se libera cuando sale del cuerpo de la instrucción lock. Eso significa que su código es no a prueba de hilos.

En otras palabras, puede estar seguro de que dos subprocesos no se ejecutarán return someList en el mismo objeto al mismo tiempo. Pero ciertamente es posible que un subproceso ejecute Add() al mismo tiempo que otro subproceso ejecutará RemoveAt(), que es lo que lo hace no seguro para subprocesos.

1

Ok, solo por el placer de hacerlo.
Hay una manera de hacer que su objeto sea seguro para los hilos, usando arquitecturas que ya son seguras para la rosca.

Por ejemplo, puede hacer que su objeto sea un objeto de rosca simple COM. El objeto COM será seguro para subprocesos, pero pagará con rendimiento (el precio de ser flojo y no administrar sus propios bloqueos).

Create a COM Object in C#

+0

Esto es como matar una mosca con una locomotora. En realidad podría funcionar, pero es más difícil hacerlo correctamente y puede presentar efectos secundarios que podrían morderlo más adelante. – svick

0

... otros dijeron ya, pero sólo para formalizar un problema un poco ...

  • En primer lugar, sugiere una lock (this) {...} 'alcance' - por ejemplo, como using(){} - solo se bloquea (esto en este caso) para variables dentro.Y eso es 'bueno' :) en realidad, como si no pudieras confiar en que todo el concepto de bloqueos/sincronización sería inútil,
  • lock (this) { return something; } es un tipo de oxímoron - está devolviendo algo que desbloquea el en el mismo momento en que regresa,
  • Y los problemas que creo que es la comprensión de cómo funciona. 'lock()' no está 'persistente' en un estado del objeto, por lo que puede devolver, etc. Mire aquí cómo está implementado How does lock work exactly? - la respuesta lo explica. Es más una 'sección crítica', es decir, usted protege ciertas partes del 'código', que usa la variable, no la variable misma. Cualquier tipo de sincronización requiere 'objetos de sincronización' para mantener los bloqueos, y para eliminarlos una vez que ya no se necesita el bloqueo. Echar un vistazo a este post https://stackoverflow.com/a/251668/417747, Esteban formulado que muy bien,

"Por último, existe la idea errónea de que bloquee (este) en realidad modifica el objeto pasado como parámetro, y de alguna manera hace es de sólo lectura o inaccesible. esto es falso. El objeto pasado como parámetro para bloquear simplemente sirve como una clave" (esto es una cita)

  • que o bien (por lo general) Lock 'código privado' de un método de clase, propiedad ... - para sincronizar el acceso a algo lo estás haciendo por dentro, y accedes a un miembro privado (otra vez por lo general) para que nadie más pueda acceder sin su código sincronizado.
  • O bien, crea una 'estructura' segura para hilos, como una lista, que ya está 'sincronizada' dentro para que pueda acceder a ella de manera segura para hilos. Pero no existen tales cosas (o no se usan, casi nunca) como cerraduras de paso o tener un lugar que bloquea la variable, mientras que la otra parte del código lo desbloquea, etc. (en ese caso es un tipo de EventWaitHandle que está más bien acostumbrado a sincronizar las cosas entre medias código 'distante' donde se dispara en otro, etc.)
  • en su caso, la elección es que creo que ir con la 'estructura sincronizada', es decir, la lista que maneja internamente,
Cuestiones relacionadas