Quiero una implementación de List<T>
como una propiedad que se pueda usar de forma segura para subprocesos sin ninguna duda.
Algo como esto:
private List<T> _list;
private List<T> MyT
{
get { // return a copy of _list; }
set { _list = value; }
}
Parece que todavía necesito devolver una copia (clonada) de la colección, por lo que si en algún lugar estamos iterando la colección y al mismo tiempo la colección está configurada, no se genera ninguna excepción.
¿Cómo implementar una propiedad de colección segura para subprocesos?
c#
collections
properties
thread-safety
Xaqron
fuente
fuente
IList<T>
(vsList<T>
)?List<T>
implementa? En caso afirmativo, ¿podría proporcionar la interfaz que necesita en lugar de preguntar sobre todo lo queList<T>
ya tiene?Respuestas:
Si está apuntando a .Net 4, hay algunas opciones en System.Collections.Concurrent Namespace
Podrías usar
ConcurrentBag<T>
en este caso en lugar deList<T>
fuente
ConcurrentBag
es una colección desordenada, por lo que a diferencia deList<T>
ella no garantiza el pedido. Además, no puede acceder a elementos por índice.Incluso cuando obtuvo la mayor cantidad de votos, por lo general no se puede tomar
System.Collections.Concurrent.ConcurrentBag<T>
como reemplazo seguro para subprocesos porqueSystem.Collections.Generic.List<T>
no está ordenado (Radek Stromský ya lo señaló).Pero hay una clase llamada
System.Collections.Generic.SynchronizedCollection<T>
que ya es desde .NET 3.0 parte del marco, pero está tan bien escondida en una ubicación donde no se espera que sea poco conocida y probablemente nunca te hayas tropezado con ella (al menos Nunca lo hice).SynchronizedCollection<T>
se compila en el ensamblado System.ServiceModel.dll (que es parte del perfil del cliente pero no de la biblioteca de clases portátil).Espero que ayude.
fuente
Creo que crear una clase ThreadSafeList de muestra sería fácil:
Simplemente clona la lista antes de solicitar un enumerador y, por lo tanto, cualquier enumeración funciona a partir de una copia que no se puede modificar mientras se ejecuta.
fuente
T
es un tipo de referencia, ¿no devolverá simplemente una nueva lista que contiene referencias a todos los objetos originales? Si ese es el caso, este enfoque aún podría causar problemas de subprocesos, ya que varios subprocesos pueden acceder a los objetos de la lista a través de diferentes "copias" de la lista.newList
que no se agregaron o eliminaron elementos que invalidaran el enumerador).Incluso la respuesta aceptada es ConcurrentBag, no creo que sea un reemplazo real de la lista en todos los casos, ya que el comentario de Radek a la respuesta dice: "ConcurrentBag es una colección desordenada, por lo que, a diferencia de List, no garantiza el pedido. Además, no puede acceder a los elementos por índice ".
Entonces, si usa .NET 4.0 o superior, una solución podría ser usar ConcurrentDictionary con integer TKey como índice de matriz y TValue como valor de matriz. Esta es la forma recomendada de reemplazar la lista en el curso Colecciones concurrentes de C # de Pluralsight . ConcurrentDictionary resuelve los dos problemas mencionados anteriormente: acceso al índice y ordenamiento (no podemos confiar en ordenar ya que es una tabla hash bajo el capó, pero la implementación actual de .NET ahorra el orden de adición de elementos).
fuente
ConcurrentDictionary
es un diccionario, no una lista. 2) No se garantiza que se mantenga el orden, como lo establece su propia respuesta, lo que contradice su razón declarada para publicar una respuesta. 3) Se vincula a un video sin incluir las citas relevantes en esta respuesta (que de todos modos podría no estar de acuerdo con su licencia).current implementation
si la documentación no lo garantiza explícitamente. La implementación puede cambiar en cualquier momento sin previo aviso.La
ArrayList
clase de C # tiene unSynchronized
método.Esto devuelve un contenedor seguro para subprocesos alrededor de cualquier instancia de
IList
. Todas las operaciones deben realizarse a través de la envoltura para garantizar la seguridad del hilo.fuente
Si observa el código fuente de List of T ( https://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs,c66df6f36c131877 ), notará que hay una clase allí (que, por supuesto, interno - ¿por qué, Microsoft, por qué?!?!) llamado SynchronizedList of T. Estoy copiando pegando el código aquí:
Personalmente, creo que sabían que se podía crear una mejor implementación usando SemaphoreSlim , pero no lo consiguieron.
fuente
_root
) en cada acceso (lectura / escritura) hace que esta sea una solución lenta. Quizás sea mejor que esta clase permanezca interna.Clear()
después de otra llamadathis[index]
pero antes de que se active el bloqueo.index
ya no es seguro de usar y lanzará una excepción cuando finalmente se ejecute.También puedes usar el más primitivo
qué bloqueo utiliza (consulte esta publicación C # Bloquear un objeto que se reasigna en el bloque de bloqueo ).
Si espera excepciones en el código, esto no es seguro, pero le permite hacer algo como lo siguiente:
Una de las cosas buenas de esto es que obtendrá el bloqueo durante la duración de la serie de operaciones (en lugar de bloquear cada operación). Lo que significa que la salida debe aparecer en los fragmentos correctos (mi uso de esto fue obtener algunos resultados en la pantalla de un proceso externo)
Realmente me gusta la simplicidad + transparencia de ThreadSafeList + que hace la parte importante para detener los bloqueos
fuente
En .NET Core (cualquier versión), puede usar ImmutableList , que tiene todas las funciones de
List<T>
.fuente
Creo que
_list.ToList()
te haré una copia. También puede consultarlo si lo necesita, como:De todos modos, msdn dice que esto es de hecho una copia y no simplemente una referencia. Ah, y sí, tendrá que bloquear el método de configuración como han señalado los demás.
fuente
Parece que muchas de las personas que encuentran esto quieren una colección de tamaño dinámico indexada segura para subprocesos. Lo más cercano y fácil que conozco sería.
Esto requeriría que se asegure de que su clave esté debidamente incriminada si desea un comportamiento de indexación normal. Si tiene cuidado, .count podría ser suficiente como clave para cualquier nuevo par clave-valor que agregue.
fuente
Sugeriría a cualquiera que se
List<T>
enfrente a escenarios de subprocesos múltiples que eche un vistazo a las Colecciones inmutables, en particular a ImmutableArray .Lo he encontrado muy útil cuando tienes:
También puede ser útil cuando necesita implementar algún tipo de comportamiento similar a una transacción (es decir, revertir una operación de inserción / actualización / eliminación en caso de error)
fuente
Aquí está la clase que solicitó:
fuente
this.GetEnumerator();
cuando @Tejs sugierethis.Clone().GetEnumerator();
?[DataContract( IsReference = true )]
?Básicamente, si desea enumerar de forma segura, debe usar lock.
Consulte MSDN sobre esto. http://msdn.microsoft.com/en-us/library/6sh2ey19.aspx
Aquí hay parte de MSDN que podría interesarle:
Los miembros públicos estáticos (compartidos en Visual Basic) de este tipo son seguros para subprocesos. No se garantiza que ningún miembro de instancia sea seguro para subprocesos.
Una lista puede admitir varios lectores al mismo tiempo, siempre que no se modifique la colección. Enumerar a través de una colección no es intrínsecamente un procedimiento seguro para subprocesos. En el raro caso en el que una enumeración compite con uno o más accesos de escritura, la única forma de garantizar la seguridad de los subprocesos es bloquear la colección durante toda la enumeración. Para permitir que varios subprocesos accedan a la colección para leer y escribir, debe implementar su propia sincronización.
fuente
Aquí está la clase para la lista segura para subprocesos sin bloqueo
fuente
Utilice la
lock
declaración para hacer esto. ( Lea aquí para obtener más información ) .Para su información, esto probablemente no sea exactamente lo que está preguntando; es probable que desee bloquear más en su código, pero no puedo asumir eso. Eche un vistazo al
lock
palabra clave y adapte su uso a su situación específica.Si es necesario, puede
lock
en ambos bloquesget
yset
usando la_list
variable que haría que no se pueda realizar una lectura / escritura al mismo tiempo.fuente
lock
declaración. Usando un ejemplo similar, podría argumentar que no deberíamos usar bucles for, ya que podría bloquear la aplicación sin apenas esfuerzo:for (int x = 0; x >=0; x += 0) { /* Infinite loop, oops! */ }
lock (this)
ylock (typeof(this))
son grandes no-no.