2010-10-26 19 views
8

En la implementación actual de CPython, hay un objeto conocido como "GIL" o "Global Interpreter Lock". Es esencialmente un mutex que evita que dos subprocesos Python ejecuten el código Python al mismo tiempo. Esto evita que dos subprocesos puedan dañar el estado del intérprete de Python, pero también evita que varios subprocesos se ejecuten realmente juntos. En esencia, si hago esto:¿Cuál es la versión del GIL de C#?

# Thread A 
some_list.append(3) 
# Thread B 
some_list.append(4) 

no puede corromper la lista, ya que en un momento dado, sólo uno de esos hilos se están ejecutando, ya que deben sostener el GIL para hacerlo. Ahora, los elementos de la lista pueden agregarse en algún orden indeterminado, pero el punto es que la lista no está dañada y siempre se agregarán dos cosas.

Por lo tanto, ahora a C#. C# esencialmente enfrenta el mismo problema que Python, entonces, ¿cómo C# previene esto? También me interesaría escuchar la historia de Java, si alguien lo sabe.


Aclaración: estoy interesado en lo que sucede sin declaraciones explícitas de bloqueo, especialmente a la máquina virtual. Soy consciente de que existen primitivas de bloqueo para Java & C# - también existen en Python: GIL no se utiliza para código de subprocesos múltiples, salvo para mantener el intérprete en buen estado. Estoy interesado en el equivalente directo de lo anterior, por lo que, en C#, si puedo recordar lo suficiente ... :-)

List<String> s; 
// Reference to s is shared by two threads, which both execute this: 
s.Add("hello"); 
// State of s? 
// State of the VM? (And if sane, how so?) 

He aquí otro ejemplo:

class A 
{ 
    public String s; 
} 
// Thread A & B 
some_A.s = some_other_value; 

// some_A's state must change: how does it change? 
// Is the VM still in good shape afterwards? 

No estoy buscando para escribir el código C# malo, entiendo las declaraciones lock. Incluso en Python, el GIL no le da código mágico multihilo: aún debe bloquear los recursos compartidos. . Pero el GIL impide "máquina virtual" de Python se corrompan - es este comportamiento que estoy interesado en

+0

usted puede estar interesado en la sección "concurrencia" de este [comparación de Python y Clojure] (http: //www.bestinclass .dk/index.clj/2009/10/python-vs-clojure-evolving.html), para una discusión del GIL y cómo el problema de la concurrencia se maneja en idiomas sin un GIL. –

+0

Según tengo entendido, es una solución de compromiso entre qué clase de problemas será más difícil de resolver. Ver mi respuesta a continuación. – pyfunc

Respuesta

4

Utilización del bloqueo, se podría hacer esto:

lock(some_list) 
{ 
    some_list.Add(3); 
} 

y en hilo de 2:

lock(some_list) 
{ 
    some_list.Add(4); 
} 

La instrucción lock asegura que el objeto dentro de la instrucción lock, some_list en este caso, solo se puede acceder mediante un único subproceso a la vez. Consulte http://msdn.microsoft.com/en-us/library/c5kehkcz(VS.80).aspx para obtener más información.

+0

Podría, y Python tiene instrucciones de bloqueo similares. Estoy más interesado en el caso de que los bloqueos no hayan terminado: ¿es posible corromper la máquina virtual subyacente? – Thanatos

+0

@Thanatos: No es la máquina virtual, sino la estructura de datos. –

+2

El resultado es que no habrá garantía de lo que sucederá. ** No ** corromperá el estado de la máquina virtual. Sin embargo, puede dañar el estado de la instancia a la que llama los métodos. Cuando no se menciona específicamente en las páginas de MSDN, la regla general es que los métodos de instancia deben estar encerrados en 'lock' y los métodos estáticos no tienen que estar encerrados en' lock''s. –

11

La mayoría de los otros lenguajes que admiten enhebrar no tienen un equivalente de Python GIL; requieren que uses mutexes, implícita o explícitamente.

+3

Además, nunca consideraría escribir código de subprocesos múltiples en un lenguaje con algo como un GIL. Es como un letrero de neón parpadeante que dice: "El subprocesamiento múltiple es demasiado difícil; nos damos por vencidos". –

+6

No consideraría escribir un programa multiproceso en un lenguaje que no pueda mantener su estructura de datos básica coherente por sí sola y confíe en los programadores para agregar bloqueos en todas partes. – piro

+2

¿Prefiere usar un lenguaje en el que es imposible que ocurra más de una cosa a la vez? Ese no es un programa multiproceso. Y no, "agregar bloqueos en todas partes" no es la única alternativa. –

1

Puede ser instructivo examinar la documentation for the Java equivalent de la clase que está discutiendo:

Tenga en cuenta que esta aplicación no está sincronizado. Si hay varios subprocesos que acceden simultáneamente a una instancia ArrayList, y al menos uno de los subprocesos modifica la lista estructuralmente, debe estar sincronizado externamente. (Una modificación estructural es cualquier operación que agrega o elimina uno o más elementos, o cambia el tamaño explícitamente de la matriz de respaldo, simplemente establecer el valor de un elemento no es una modificación estructural). Esto normalmente se logra sincronizando en algún objeto que naturalmente encapsula el lista. Si no existe tal objeto, la lista debe ser "ajustada" utilizando el método Collections.synchronizedList.Esto se hace mejor en tiempo de creación, para evitar el acceso no sincronizado accidental a la lista:

List list = Collections.synchronizedList(new ArrayList(...)); 

Los iteradores devuelto por iterador y ListIterator métodos de esta clase son fail-fast: si la lista se modifica estructuralmente en cualquier momento después de que se crea el iterador, de cualquier forma, excepto a través de los propios métodos remove o add del iterador, el iterador lanzará un ConcurrentModificationException. Por lo tanto, frente a la modificación concurrente, el iterador falla rápida y limpiamente, en lugar de arriesgarse a un comportamiento arbitrario y no determinista en un tiempo indeterminado en el futuro.

Tenga en cuenta que el comportamiento a prueba de fallos de un iterador no se puede garantizar, ya que, en términos generales, es imposible hacer ninguna garantía dura en presencia de modificaciones concurrentes no sincronizadas. Los iteradores a prueba de fallos lanzan ConcurrentModificationException sobre la base del mejor esfuerzo. Por lo tanto, sería incorrecto escribir un programa que dependiera de esta excepción para su corrección: el comportamiento a prueba de fallas de los iteradores debería usarse solo para detectar los errores.

3

C# no tiene un equivalente de GIL a Python.

Aunque se enfrentan al mismo problema, sus objetivos de diseño los hacen diferentes.

Con GIL, CPython asegura que las operaciones suche como anexar una lista a partir de dos hilos es simple. Que también significa que permitiría que solo se ejecute un subproceso en cualquier momento. Este hace que las listas y los diccionarios sean seguros. Aunque hace que el trabajo sea más simple e intuitivo, lo hace más difícil de aprovechar la ventaja del multiproceso en multinúcleos.

Sin GIL, C# hace lo contrario. Asegura que la carga de la integridad está en el desarrollador del programa pero le permite tomar ventaja de ejecutar múltiples hilos simultáneamente.

De acuerdo con uno de la discusión -

El GIL en CPython es puramente una opción de diseño de tener un gran bloqueo frente a un bloqueo por objeto y sincronización para asegurarse de que los objetos se mantienen en un estado coherente. Esto consiste en una compensación: renunciar a la potencia máxima de multihilo.

Ha sido que la mayoría de los problemas no sufren esta desventaja y hay bibliotecas que lo ayudan a resolver este problema exclusivamente cuando se requiere . Eso significa que para una cierta clase de problemas, la carga para utilizar el multinúcleo es pasado al desarrollador para que el resto pueda disfrutar del enfoque más simple e intuitivo .

Nota: Otras implementaciones como IronPython no tienen GIL.

+0

@Daniel DiPaolo: Muchas gracias. De alguna manera, la cita no funciona para mí cuando selecciono un texto y trato de citarlo. – pyfunc

+0

Extraño, bueno, el secreto es que es simplemente '>' al comienzo de una línea que obliga al blockquote en lugar de formatear el código. Tenga en cuenta que aún puede hacer el formateo del código dentro de una comilla de bloque espaciando apropiadamente con un '>' al comienzo de la línea. –

0

Las estructuras de datos más complejas (por ejemplo, listas) pueden corromperse cuando se utilizan sin bloquear varios hilos.

Dado que los cambios de referencias son atómicos, una referencia siempre permanece como una referencia válida.

Pero hay un problema al interactuar con el código de seguridad crítica. Por lo que cualquier estructuras de datos utilizadas por código crítico sumo, uno de los siguientes:

  • inaccesible desde código no confiable, y bloqueado/utilizado correctamente por el código de confianza
  • Inmutable (clase String)
  • Copiado antes de su uso (parámetros valuetype)
  • escrito en el código de confianza y usos de bloqueo interno para garantizar un estado seguro

Por ejemplo código crítico no puede confiar en una lista accesible desde código no confiable. Si se pasa en una lista, tiene que crear una copia privada, hacer las verificaciones de precondición en la copia y luego operar la copia.

0

voy a tomar una conjetura salvaje en lo que la pregunta realmente significa ...

En las estructuras de datos de Python en el intérprete se dañan debido a Python utiliza una forma de recuento de referencias.

Tanto C# como Java usan la recolección de elementos no utilizados y, de hecho, hacen usan un bloqueo global cuando se hace una recolección de pila completa.

Los datos se pueden marcar y mover entre "generaciones" sin bloqueo. Pero para limpiarlo todo debe detenerse. Con suerte, una parada muy breve, pero un punto.

Aquí es un enlace interesante sobre la recogida de basura CLR a partir de 2007:
http://vineetgupta.spaces.live.com/blog/cns!8DE4BDC896BEE1AD!1104.entry

Cuestiones relacionadas