Editar para intro:
Sabemos que un parámetro de referencia en C# pasa una referencia a una variable, lo que permite la propia variable externa a ser cambiado dentro de un método llamado. Pero, ¿se maneja la referencia como un puntero C (leyendo los contenidos actuales de la variable original con cada acceso a ese parámetro y cambiando la variable original con cada modificación del parámetro), o puede el método invocado basarse en una referencia consistente para el duración de la llamada? El primero saca a relucir algunos problemas de seguridad de hilo. En particular:¿Los parámetros de las referencias .NET son seguros para la ejecución de subprocesos o son vulnerables al acceso multiproceso no seguro?
He escrito un método estático en C# que pasa un objeto por referencia:
public static void Register(ref Definition newDefinition) { ... }
La persona que llama proporciona una completado pero que aún no es registrado Definition
objeto, y después de cierta consistencia que la comprobación "registrar" la definición que proporcionaron. Sin embargo, si ya hay una definición con la misma clave, no puede registrar la nueva y en su lugar su referencia se actualiza al Definition
"oficial" para esa clave.
Queremos que sea rigurosamente seguro para subprocesos, pero nos viene a la mente un escenario patológico. Supongamos que el cliente (usando nuestra biblioteca) comparte la referencia de un modo no seguro para subprocesos, tales como el uso de un miembro estático en lugar de una variable local:
private static Definition riskyReference = null;
Si un hilo establece riskyReference = new Definition("key 1");
, llena la definición, y llama a nuestro Definition.Register(ref riskyReference);
, mientras que otro subproceso también decide establecer riskyReference = new Definition("key 2");
, ¿estamos seguros de que en nuestro método de Registro la referencia newDefinition
que estamos manejando no será modificada por otros hilos (porque la referencia al objeto se copió en y será ¿se copian cuando volvemos?), o puede ese otro hilo reemplazar el objeto sobre nosotros en el medio de nuestra ejecución (si estamos haciendo referencia a un puntero a la ubicación de almacenamiento original ???) y así romper nuestra comprobación de la cordura?
Tenga en cuenta que esto es diferente de los cambios en el objeto subyacente en sí, que por supuesto son posibles para un tipo de referencia (clase), pero pueden protegerse fácilmente mediante el bloqueo apropiado dentro de esa clase. Sin embargo, no podemos guardar cambios en el espacio variable del cliente externo. Tendríamos que hacer nuestra propia copia del parámetro en la parte superior del método y sobrescribir el parámetro en la parte inferior (por ejemplo), pero parece que tiene más sentido para el compilador hacer por nosotros dada la locura de manejar una referencia insegura.
Por lo tanto, tendería a pensar que el compilador puede copiar y copiar la referencia para que el método maneje una referencia consistente al objeto original (hasta que cambie su propia referencia cuando lo desee) independientemente de lo que pueda estar pasando con la ubicación original en otros hilos. Pero estamos teniendo problemas para encontrar una respuesta definitiva sobre ese punto en la documentación y discusión de los parámetros de referencia.
¿Alguien puede calmar mi preocupación con una cita definitiva?
Editar para la conclusión: (! Gracias Marc)
Después de haber confirmado con un ejemplo de código multi-hilo y pensar en ello aún más, tiene sentido que de hecho es la no-automática-multi-hilo comportamiento cuales Me preocuparon. Un punto de "ref" es pasar estructuras grandes por referencia en lugar de copiarlas.Otra razón es que puede querer para configurar una monitorización a largo plazo de una variable y debe pasarle una referencia que verá los cambios a la variable (por ejemplo, cambiar entre nulo y un objeto activo), que es automático copiar/enviar/copiar no permitiría.
Por lo tanto, para que nuestra Register
método robusto frente a la locura cliente, podríamos implementarlo como:
public static void Register(ref Definition newDefinition) {
Definition theDefinition = newDefinition; // Copy in.
//... Sanity checks, actual work...
//...possibly changing theDefinition to a new Definition instance...
newDefinition = theDefinition; // Copy out.
}
Habían todavía tienen sus propios problemas de threads en cuanto a lo que terminan siendo, pero al menos su locura no rompería nuestro propio proceso de control de la cordura y posiblemente pasaría un mal estado más allá de nuestros controles.
bien, ese ejemplo * no * mostrar que los parámetros de árbitro en C# * no * copias compatibles con el proceso, por lo que el cliente haciendo algo estúpido y patológica podría cambiar qué objeto estamos manejo en el medio de nuestro método. Tendríamos que hacer nuestra propia copia de entrada y salida si queremos protegernos de ello. ¡Gracias! –