¿Existen zombies ... en .NET?

385

Estaba teniendo una discusión con un compañero de equipo sobre bloquear en .NET. Es un tipo realmente brillante con una amplia experiencia tanto en programación de nivel inferior como de nivel superior, pero su experiencia con la programación de nivel inferior supera con creces la mía. De todos modos, argumentó que el bloqueo de .NET debe evitarse en los sistemas críticos que se espera que estén sometidos a una gran carga, si es posible, para evitar la pequeña posibilidad de que un "hilo zombie" estrelle un sistema. Usualmente uso el bloqueo y no sabía qué era un "hilo zombie", así que pregunté. La impresión que obtuve de su explicación es que un hilo zombie es un hilo que ha terminado pero que de alguna manera aún conserva algunos recursos. Un ejemplo que dio de cómo un hilo zombie podría romper un sistema fue que un hilo comienza un procedimiento después de bloquear un objeto, y luego termina en algún momento antes de que se pueda liberar el bloqueo. Esta situación tiene el potencial de bloquear el sistema, porque eventualmente, los intentos de ejecutar ese método darán como resultado que todos los hilos esperen el acceso a un objeto que nunca será devuelto, porque el hilo que está usando el objeto bloqueado está muerto.

Creo que entendí la esencia de esto, pero si estoy fuera de la base, por favor hágamelo saber. El concepto tenía sentido para mí. No estaba completamente convencido de que este fuera un escenario real que podría suceder en .NET. Nunca antes había oído hablar de "zombis", pero sí reconozco que los programadores que han trabajado en profundidad en los niveles inferiores tienden a tener una comprensión más profunda de los fundamentos de la informática (como el subprocesamiento). Sin embargo, definitivamente veo el valor en el bloqueo, y he visto a muchos programadores de clase mundial aprovechar el bloqueo. También tengo una capacidad limitada para evaluar esto por mí mismo porque sé que la lock(obj)declaración es realmente solo azúcar sintáctica para:

bool lockWasTaken = false;
var temp = obj;
try { Monitor.Enter(temp, ref lockWasTaken); { body } }
finally { if (lockWasTaken) Monitor.Exit(temp); }

y porque Monitor.Entery Monitor.Exitestán marcados extern. Parece concebible que .NET realice algún tipo de procesamiento que proteja los hilos de la exposición a los componentes del sistema que podrían tener este tipo de impacto, pero eso es puramente especulativo y probablemente solo se base en el hecho de que nunca he oído hablar de "hilos de zombis" antes de. Entonces, espero poder obtener algunos comentarios sobre esto aquí:

  1. ¿Existe una definición más clara de "hilo zombie" que la que he explicado aquí?
  2. ¿Pueden aparecer hilos zombie en .NET? (¿Por qué por qué no?)
  3. Si corresponde, ¿cómo podría forzar la creación de un hilo zombie en .NET?
  4. Si corresponde, ¿cómo puedo aprovechar el bloqueo sin arriesgar un escenario de subproceso zombie en .NET?

Actualizar

Hice esta pregunta hace poco más de dos años. Hoy sucedió esto:

El objeto está en estado zombie.

Smartcaveman
fuente
8
¿Estás seguro de que tu compañero no habla de bloqueo?
Andreas Niedermair
10
@AndreasNiedermair: sé lo que es un punto muerto y claramente no se trataba de un mal uso de esa terminología. El bloqueo se mencionó en la conversación y era claramente distinto de un "hilo zombie". Para mí, la distinción principal es que un bloqueo muerto tiene una dependencia bidireccional no resuelta, mientras que el hilo zombie es unidireccional y requiere un proceso terminado. Si no está de acuerdo y cree que hay una mejor manera de ver estas cosas, explique
smartcaveman
19
Creo que el término "zombie" en realidad proviene de un fondo UNIX, como en "proceso zombie", ¿verdad? Hay una definición clara de un "proceso zombie" en UNIX: describe un proceso hijo que ha finalizado pero donde el padre del proceso hijo todavía necesita "liberar" el proceso hijo (y sus recursos) llamando waito waitpid. El proceso hijo se denomina "proceso zombie". Ver también howtogeek.com/119815
hogliux
99
Si parte de su programa falla, dejando el programa en un estado indefinido, entonces, por supuesto, eso puede causar problemas con el resto de su programa. Lo mismo puede suceder si maneja incorrectamente las excepciones en un programa de subproceso único. El problema no es con los hilos, el problema es que tiene un estado global mutable y que no está manejando adecuadamente la terminación inesperada del hilo. Su compañero de trabajo "realmente brillante" está totalmente fuera de lugar en este caso.
Jim Mischel
99
"Desde las primeras computadoras, siempre ha habido fantasmas en la máquina. Segmentos aleatorios de código que se han agrupado para formar protocolos inesperados ..."
Chris Laplante

Respuestas:

242
  • ¿Existe una definición más clara de "hilo zombie" que la que he explicado aquí?

Parece una buena explicación para mí: un hilo que ha terminado (y por lo tanto ya no puede liberar ningún recurso), pero cuyos recursos (por ejemplo, identificadores) todavía están alrededor y (potencialmente) causan problemas.

  • ¿Pueden aparecer hilos zombie en .NET? (¿Por qué por qué no?)
  • Si corresponde, ¿cómo podría forzar la creación de un hilo zombie en .NET?

Seguro que sí, mira, ¡hice uno!

[DllImport("kernel32.dll")]
private static extern void ExitThread(uint dwExitCode);

static void Main(string[] args)
{
    new Thread(Target).Start();
    Console.ReadLine();
}

private static void Target()
{
    using (var file = File.Open("test.txt", FileMode.OpenOrCreate))
    {
        ExitThread(0);
    }
}

Este programa inicia un hilo Targetque abre un archivo y luego se mata inmediatamente usando ExitThread. El hilo zombie resultante nunca liberará el identificador del archivo "test.txt" y, por lo tanto, el archivo permanecerá abierto hasta que finalice el programa (puede consultar con el explorador de procesos o similar). El identificador de "test.txt" no se lanzará hasta que GC.Collectse llame; resulta que es aún más difícil de lo que pensé para crear un hilo zombie que filtra los identificadores)

  • Si corresponde, ¿cómo puedo aprovechar el bloqueo sin arriesgar un escenario de subproceso zombie en .NET?

¡No hagas lo que acabo de hacer!

Siempre que su código se limpie correctamente después de sí mismo (use Safe Handles o clases equivalentes si trabaja con recursos no administrados), y siempre y cuando no salga de su camino para matar hilos de maneras extrañas y maravillosas (la forma más segura es solo para nunca matar subprocesos: deje que terminen normalmente, o mediante excepciones si es necesario), la única forma en que tendrá algo parecido a un hilo zombie es si algo ha salido muy mal (por ejemplo, algo sale mal en el CLR).

De hecho, es realmente sorprendente crear un hilo zombie (tuve que P / Invocar en una función que esencialmente te dice en la documentación que no lo llames fuera de C). Por ejemplo, el siguiente código (horrible) en realidad no crea un hilo zombie.

static void Main(string[] args)
{
    var thread = new Thread(Target);
    thread.Start();
    // Ugh, never call Abort...
    thread.Abort();
    Console.ReadLine();
}

private static void Target()
{
    // Ouch, open file which isn't closed...
    var file = File.Open("test.txt", FileMode.OpenOrCreate);
    while (true)
    {
        Thread.Sleep(1);
    }
    GC.KeepAlive(file);
}

A pesar de cometer algunos errores bastante horribles, el identificador de "test.txt" todavía se cierra tan pronto como Abortse lo llama (como parte del finalizador para el filecual debajo de las cubiertas usa SafeFileHandle para ajustar su identificador de archivo)

El ejemplo de bloqueo en la respuesta de C.Evenhuis es probablemente la forma más fácil de no liberar un recurso (un bloqueo en este caso) cuando un subproceso se termina de una manera no extraña, pero eso se soluciona fácilmente mediante el uso de una lockdeclaración o poniendo el lanzamiento en un finallybloque.

Ver también

Justin
fuente
3
Recuerdo que cuando jugaba a guardar cosas en Excel usando un trabajador de fondo, no liberaba todos los recursos todo el tiempo (porque simplemente me salteé la depuración, etc.). en el administrador de tareas vi luego unos 50 procesos de Excel. ¿Creé procesos zombieexcel?
Stefan
3
@Justin - +1 - Gran respuesta. Sin ExitThreadembargo, soy un poco escéptico de tu llamada. Obviamente, funciona, pero se siente más como un truco inteligente que como un escenario realista. Uno de mis objetivos es aprender qué no hacer para no crear accidentalmente hilos zombies con código .NET. Probablemente podría haber descubierto que llamar al código C ++ que se sabe que causa ese problema desde el código .NET produciría el efecto deseado. Obviamente sabes mucho sobre estas cosas. ¿Conoces otros casos (posiblemente extraños, pero no lo suficientemente extraños como para nunca suceder involuntariamente) con el mismo resultado?
smartcaveman
3
Entonces la respuesta es 'no en c # 4', ¿verdad? Si tiene que saltar fuera del CLR para obtener un hilo zombie, no parece un problema .Net.
Gusdor
44
@Stefan Yo diría que definitivamente no. He hecho lo que describiste muchas veces. Los procesos de Excel no son zombies, ya que todavía se están ejecutando, aunque no son accesibles de inmediato a través de la interfaz de usuario normal. Debería poder recuperarlos mediante llamadas a GetObject . Set excelInstance = GetObject(, "Excel.Application")
Daniel
77
"El subproceso zombie resultante nunca liberará el identificador del archivo" test.txt "y, por lo tanto, el archivo permanecerá abierto hasta que el programa finalice" es incorrecto. Pequeña prueba: `static void Main (string [] args) {new Thread (GcCollect) .Start (); nuevo hilo (Target) .Start (); Console.ReadLine (); } Destino vacío estático privado () {using (var file = File.Open ("test.txt", FileMode.OpenOrCreate)) {ExitThread (0); }} privado estático vacío GcCollect () {while (true) {Thread.Sleep (10000); GC.Collect (); }} `
Sinix
46

Limpié un poco mi respuesta, pero dejé la original a continuación como referencia.

Es la primera vez que escucho sobre el término zombis, así que asumiré que su definición es:

Un hilo que ha terminado sin liberar todos sus recursos

Entonces, dada esa definición, entonces sí, puede hacerlo en .NET, como con otros lenguajes (C / C ++, java).

Sin embargo , no creo que esta sea una buena razón para no escribir código de misión crítica con hilos en .NET. Puede haber otras razones para decidir en contra de .NET, pero descartar .NET solo porque puede tener hilos zombie de alguna manera no tiene sentido para mí. Los hilos de Zombie son posibles en C / C ++ (incluso diría que es mucho más fácil desordenar en C) y muchas aplicaciones críticas y enhebradas están en C / C ++ (comercio de alto volumen, bases de datos, etc.).

Conclusión Si está en el proceso de decidir qué idioma utilizar, le sugiero que tenga en cuenta el panorama general: rendimiento, habilidades de equipo, programación, integración con aplicaciones existentes, etc. Seguro, los hilos zombies son algo en lo que debe pensar , pero dado que es muy difícil cometer este error en .NET en comparación con otros lenguajes como C, creo que esta preocupación se verá eclipsada por otras cosas como las mencionadas anteriormente. ¡Buena suerte!

Original Response Zombies puede existir si no escribe el código de subproceso adecuado. Lo mismo es cierto para otros lenguajes como C / C ++ y Java. Pero esta no es una razón para no escribir código roscado en .NET.

Y al igual que con cualquier otro idioma, conozca el precio antes de usar algo. También ayuda saber qué sucede debajo del capó para poder prever cualquier problema potencial.

El código confiable para sistemas de misión crítica no es fácil de escribir, independientemente del idioma en el que se encuentre. Pero estoy seguro de que no es imposible hacerlo correctamente en .NET. También AFAIK, el enhebrado .NET no es tan diferente del enhebrado en C / C ++, usa (o está construido a partir de) las mismas llamadas del sistema, excepto algunas construcciones específicas de .net (como las versiones livianas de RWL y clases de eventos).

† La primera vez que escuché hablar del término zombis, pero según tu descripción, tu colega probablemente significó un hilo que terminó sin liberar todos los recursos. Esto podría causar un punto muerto, pérdida de memoria o algún otro efecto secundario negativo. Obviamente, esto no es deseable, pero señalar .NET debido a esta posibilidad probablemente no sea una buena idea, ya que también es posible en otros lenguajes. Incluso diría que es más fácil equivocarse en C / C ++ que en .NET (especialmente en C donde no tiene RAII), pero muchas aplicaciones críticas están escritas en C / C ++, ¿verdad? Por lo tanto, realmente depende de sus circunstancias individuales. Si desea extraer cada onza de velocidad de su aplicación y desea acercarse lo más posible al metal desnudo, entonces .NET podríaNo sea la mejor solución. Si tiene un presupuesto ajustado e interactúa mucho con los servicios web / bibliotecas .net existentes / etc., entonces .NET puede ser una buena opción.

Jerahmeel
fuente
(1) No estoy seguro de cómo descubrir qué sucede debajo del capó cuando llego a externmétodos sin salida . Si tienes una sugerencia, me encantaría escucharla. (2) Estoy de acuerdo en que es posible hacerlo en .NET. Me gustaría creer que es posible con el bloqueo, pero aún no he encontrado una respuesta satisfactoria para justificar eso hoy
smartcaveman
1
@smartcaveman si lo que quiere decir con lockinges la lockpalabra clave, entonces probablemente no, ya que serializa la ejecución. Para maximizar el rendimiento, tendría que usar las construcciones correctas dependiendo de las características de su código. Diría que si puede escribir código confiable en c / c ++ / java / lo que sea usando pthreads / boost / thread pools / lo que sea, entonces también puede escribirlo en C #. Pero si no puede escribir código confiable en cualquier idioma usando cualquier biblioteca, entonces dudo que escribir en C # sea diferente.
Jerahmeel
@smartcaveman en cuanto a averiguar qué hay debajo del capó, google ayuda a los montones y si su problema es demasiado exótico para encontrar en la web, el reflector es muy útil. Pero para las clases de subprocesos, encuentro que la documentación de MSDN es muy útil. Y muchos de ellos son solo envoltorios para las mismas llamadas al sistema que usa en C anway.
Jerahmeel
1
@smartcaveman en absoluto, eso no es lo que quise decir. Lo siento si se encontró de esa manera. Lo que quería decir es que no debe ser demasiado rápido para escribir .NET al escribir una aplicación crítica. Claro, podrías hacer cosas que pueden ser mucho peores que los hilos de zombies (que creo que son solo hilos que no liberaron recursos no administrados, lo que puede suceder totalmente en otros idiomas: stackoverflow.com/questions/14268080/… ), pero De nuevo, eso no significa que .NET no sea una solución viable.
Jerahmeel
2
No creo que .NET sea una mala tecnología en absoluto. En realidad es mi marco de desarrollo primario. Pero, debido a esto, creo que es importante que comprenda los defectos a los que es susceptible. Básicamente, estoy señalando .NET, pero porque me gusta, no porque no me guste. (Y no te preocupes hermano, I + 1-ed you)
smartcaveman
26

En este momento, la mayor parte de mi respuesta ha sido corregida por los comentarios a continuación. No eliminaré la respuesta porque necesito los puntos de reputación porque la información en los comentarios puede ser valiosa para los lectores.

Immortal Blue señaló que en .NET 2.0 y los finallybloques superiores son inmunes a los abortos de subprocesos. Y como comentó Andreas Niedermair, este puede no ser un hilo zombie real, pero el siguiente ejemplo muestra cómo abortar un hilo puede causar problemas:

class Program
{
    static readonly object _lock = new object();

    static void Main(string[] args)
    {
        Thread thread = new Thread(new ThreadStart(Zombie));
        thread.Start();
        Thread.Sleep(500);
        thread.Abort();

        Monitor.Enter(_lock);
        Console.WriteLine("Main entered");
        Console.ReadKey();
    }

    static void Zombie()
    {
        Monitor.Enter(_lock);
        Console.WriteLine("Zombie entered");
        Thread.Sleep(1000);
        Monitor.Exit(_lock);
        Console.WriteLine("Zombie exited");
    }
}

Sin embargo, cuando se usa un lock() { }bloque, finallyaún se ejecutará cuando a ThreadAbortExceptionse dispara de esa manera.

La siguiente información, como resultado, solo es válida para .NET 1 y .NET 1.1:

Si dentro del lock() { }bloque ocurre otra excepción, y ThreadAbortExceptionllega exactamente cuando el finallybloque está a punto de ejecutarse, el bloqueo no se libera. Como mencionó, el lock() { }bloque se compila como:

finally 
{
    if (lockWasTaken) 
        Monitor.Exit(temp); 
}

Si otro hilo llama Thread.Abort()dentro del finallybloque generado , es posible que no se libere el bloqueo.

C.Evenhuis
fuente
2
estás hablando lock()pero no puedo ver ningún uso de esto ... entonces, ¿cómo está conectado esto? este es un uso "incorrecto" de Monitor.Entery Monitor.Exit(sin el uso de tryy finally)
Andreas Niedermair
44
No llamaría a eso un hilo zombie: este es simplemente un uso incorrecto de Monitor.Entery Monitor.Exitsin el uso adecuado de tryy finally, de todos modos, su escenario bloqueará otros hilos que podrían aferrarse _lock, por lo que hay un escenario de bloqueo, no necesariamente un hilo zombie ... Además, no está liberando el bloqueo Main... pero, oye ... tal vez el OP se está bloqueando por bloqueo en lugar de hilos de zombies :)
Andreas Niedermair
1
@AndreasNiedermair quizás la definición de un hilo zombie no es exactamente lo que pensaba. Quizás podamos llamar a esto un "hilo que ha terminado la ejecución pero no ha liberado todos los recursos". Tentado a eliminar y hacer mi tarea, pero manteniendo la respuesta por el parecido con el escenario del OP.
C.Evenhuis
2
sin ofender aquí! en realidad, su respuesta me hizo pensar en ello :) ya que el OP habla explícitamente de que no se desbloquean los bloqueos, creo que su compañero habló de bloqueo muerto, porque un bloqueo no es un recurso real que está vinculado a un hilo ( es compartida ... nona) - y por lo tanto cualquier tema "zombi" podría ser evitado por pegar a las mejores prácticas y el uso locko adecuada try/ finally-usage
Andreas Niedermair
1
@ C.Evenhuis: no estoy seguro de que mi definición sea precisa. Parte de por qué hago la pregunta es aclarar esto. Creo que el concepto se menciona en gran medida en C / C ++
smartcaveman
24

No se trata de hilos de Zombie, pero el libro Effective C # tiene una sección sobre la implementación de IDisposable, (ítem 17), que habla sobre los objetos de Zombie que pensé que podrían encontrar interesantes.

Recomiendo leer el libro en sí, pero lo esencial es que si tienes una clase que implementa IDisposable o que contiene un Desctructor, lo único que debes hacer es liberar recursos. Si hace otras cosas aquí, entonces existe la posibilidad de que el objeto no se recolecte basura, pero tampoco será accesible de ninguna manera.

Da un ejemplo similar al siguiente:

internal class Zombie
{
    private static readonly List<Zombie> _undead = new List<Zombie>();

    ~Zombie()
    {
        _undead.Add(this);
    }
}

Cuando se llama al destructor en este objeto, se coloca una referencia a sí mismo en la lista global, lo que significa que permanece vivo y en memoria durante la vida del programa, pero no es accesible. Esto puede significar que los recursos (particularmente los recursos no administrados) pueden no liberarse por completo, lo que puede causar todo tipo de problemas potenciales.

Un ejemplo más completo está debajo. Para cuando se alcanza el bucle foreach, tiene 150 objetos en la lista de No muertos, cada uno con una imagen, pero la imagen ha sido GC'd y obtiene una excepción si intenta usarla. En este ejemplo, obtengo una excepción ArgumentException (el parámetro no es válido) cuando intento hacer algo con la imagen, ya sea que trate de guardarla o incluso ver dimensiones como la altura y el ancho:

class Program
{
    static void Main(string[] args)
    {
        for (var i = 0; i < 150; i++)
        {
            CreateImage();
        }

        GC.Collect();

        //Something to do while the GC runs
        FindPrimeNumber(1000000);

        foreach (var zombie in Zombie.Undead)
        {
            //object is still accessable, image isn't
            zombie.Image.Save(@"C:\temp\x.png");
        }

        Console.ReadLine();
    }

    //Borrowed from here
    //http://stackoverflow.com/a/13001749/969613
    public static long FindPrimeNumber(int n)
    {
        int count = 0;
        long a = 2;
        while (count < n)
        {
            long b = 2;
            int prime = 1;// to check if found a prime
            while (b * b <= a)
            {
                if (a % b == 0)
                {
                    prime = 0;
                    break;
                }
                b++;
            }
            if (prime > 0)
                count++;
            a++;
        }
        return (--a);
    }

    private static void CreateImage()
    {
        var zombie = new Zombie(new Bitmap(@"C:\temp\a.png"));
        zombie.Image.Save(@"C:\temp\b.png");
    }
}

internal class Zombie
{
    public static readonly List<Zombie> Undead = new List<Zombie>();

    public Zombie(Image image)
    {
        Image = image;
    }

    public Image Image { get; private set; }

    ~Zombie()
    {
        Undead.Add(this);
    }
}

Una vez más, soy consciente de que estabas preguntando sobre hilos de zombies en particular, pero el título de la pregunta es sobre zombies en .net, ¡y me acordé de esto y pensé que otros podrían encontrarlo interesante!

JMK
fuente
1
Esto es interesante. ¿Está _undeaddestinado a ser estático?
smartcaveman
1
Así que lo intenté, con algunas impresiones del objeto "destruido". Lo trata como un objeto normal. ¿Hay algo problemático en hacer esto?
smartcaveman
1
He actualizado mi respuesta con un ejemplo que, con suerte, demuestra el problema un poco más claramente.
JMK
1
No sé por qué te votaron mal. Lo encontré útil. Aquí hay una pregunta: ¿qué tipo de excepción vas a obtener? No espero que sea una NullReferenceException, porque tengo la sensación de que lo que falta debe estar más relacionado con la máquina que con la aplicación. ¿Es esto correcto?
smartcaveman
44
Seguramente no es tanto IDisposableese es el problema. Me preocupan los finalizadores (destructores), pero el solo hecho de IDisposableno hacer que un objeto vaya a la cola del finalizador y arriesgue este escenario zombie. Esta advertencia se refiere a los finalizadores, y podrían llamarse métodos de eliminación. Hay ejemplos donde IDisposablese usa en tipos sin finalizadores. El sentimiento debería ser para la limpieza de recursos, pero eso podría ser una limpieza de recursos no trivial. RX utiliza válidamente IDisposablepara la limpieza de suscripciones y puede llamar a otros recursos posteriores. (Yo tampoco voté en contra ...)
James World
21

En sistemas críticos bajo una gran carga, escribir código sin bloqueo es mejor principalmente debido a las mejoras de rendimiento. Mire cosas como LMAX y cómo aprovecha la "simpatía mecánica" para grandes discusiones sobre esto. Sin embargo, ¿te preocupan los hilos de zombis? Creo que es un caso límite que es solo un error que se debe solucionar, y no es una razón suficiente para no usarlo lock.

¡Parece que tu amigo solo es elegante y hace alarde de su conocimiento de una oscura y exótica terminología para mí! En todo el tiempo que estuve ejecutando los laboratorios de rendimiento en Microsoft UK, nunca encontré una instancia de este problema en .NET.

James World
fuente
2
Creo que diferentes conjuntos de experiencias nos vuelven paranoicos sobre diferentes errores. Reconoció que es un caso extremo al explicarlo, pero si realmente es un caso límite y no nunca, me gustaría al menos entenderlo un poco mejor. Gracias por su aporte
Smartcaveman
1
Lo suficientemente justo. ¡No quisiera que te preocupes desproporcionadamente por la lockdeclaración!
James World
1
Estaría mucho menos preocupado si tuviera una comprensión más profunda de este problema.
smartcaveman
44
Voté porque estás tratando de deshacerte de algunos hilos FUD, de los cuales hay demasiado. Lock-FUD requerirá más trabajo para eliminar :) Simplemente me avergüenzo cuando los desarrolladores toman un spinlock ilimitado, (por rendimiento), para que puedan copiar 50K de datos en una cola amplia.
Martin James
3
Sí, tanto en general como en particular. Escribir el código correcto sin bloqueo es tan desafiante como la programación se vuelve. En mi experiencia para la gran mayoría de los casos, los lockbloques grandes y relativamente fáciles de comprender son relativamente buenos. Evitaría introducir la complejidad en aras de la optimización del rendimiento hasta que sepa que lo necesita.
James World el
3

1. ¿Existe una definición más clara de "hilo zombie" que la que he explicado aquí?

Estoy de acuerdo en que existen "Zombie Threads", es un término para referirse a lo que sucede con los Threads que se quedan con recursos que no dejan ir y, sin embargo, no mueren por completo, de ahí el nombre de "zombie", por lo que su ¡La explicación de esta referencia es bastante acertada!

¿Pueden aparecer hilos de zombies en .NET? (¿Por qué por qué no?)

Sí, pueden ocurrir. Es una referencia, y en realidad Windows se refiere como "zombie": MSDN usa la palabra "Zombie" para procesos / hilos muertos

Suceder con frecuencia es otra historia, y depende de tus técnicas y prácticas de codificación, en cuanto a ti, que te gusta Thread Locking y lo has hecho por un tiempo, ni siquiera me preocuparía de que te ocurriera ese escenario.

Y sí, como @KevinPanko mencionó correctamente en los comentarios, "Zombie Threads" provienen de Unix, razón por la cual se usan en XCode-ObjectiveC y se conocen como "NSZombie" y se usan para la depuración. Se comporta más o menos de la misma manera ... la única diferencia es que un objeto que debería haber muerto se convierte en un "Objeto Zombie" para la depuración en lugar del "Hilo Zombie" que podría ser un problema potencial en su código.

SH
fuente
1
Pero la forma en que MSDN usa zombie es muy diferente de la forma en que esta pregunta lo está usando.
Ben Voigt
1
Ah, sí, estoy de acuerdo, pero estaba señalando que incluso MSDN se refiere a los hilos como Zombie Threads cuando está muerto. y que de hecho pueden ocurrir.
SH
44
Claro, pero se refiere a algún otro código que aún mantiene un identificador en el subproceso, no el subproceso que retiene los identificadores en los recursos cuando salió. Su primera oración, de acuerdo con la definición en la pregunta, es lo que está mal.
Ben Voigt
1
Ahh, entiendo tu punto. Para ser sincero, ni siquiera me di cuenta de eso. Me estaba enfocando en su punto de que la definición existe, independientemente de cómo ocurra. Recuerde que la definición existe debido a lo que le sucede al hilo y no a cómo se hace.
SH
0

Puedo hacer hilos de zombies con bastante facilidad.

var zombies = new List<Thread>();
while(true)
{
    var th = new Thread(()=>{});
    th.Start();
    zombies.Add(th);
}

Esto pierde las asas del hilo (para Join() ). Es solo otra pérdida de memoria en lo que a nosotros respecta en el mundo administrado.

Ahora bien, matar un hilo de una manera que realmente mantenga bloqueos es un dolor en la parte trasera, pero es posible. El otro tipo ExitThread()hace el trabajo. Como descubrió, el gc limpió el identificador del archivo, pero no lockalrededor de un objeto. ¿Pero por qué harías eso?

Joshua
fuente