Os zumbis existem ... no .NET?

385

Eu estava tendo uma discussão com um colega de equipe sobre o bloqueio no .NET. Ele é um cara realmente brilhante, com uma extensa experiência em programação de nível inferior e superior, mas sua experiência com programação de nível inferior excede em muito a minha. De qualquer forma, ele argumentou que o bloqueio do .NET deve ser evitado em sistemas críticos que devem estar sob carga pesada, se possível, a fim de evitar a possibilidade reconhecidamente pequena de um "encadeamento zumbi" travar um sistema. Eu costumo usar o bloqueio e não sabia o que era um "fio de zumbi", então perguntei. A impressão que tive da explicação dele é que um thread de zumbi foi encerrado, mas de alguma forma ainda mantém alguns recursos. Ele deu um exemplo de como um encadeamento de zumbis poderia quebrar um sistema, quando um encadeamento inicia algum procedimento após o bloqueio de algum objeto, e, em algum momento, é encerrado antes que o bloqueio possa ser liberado. Essa situação tem o potencial de travar o sistema, porque, eventualmente, as tentativas de executar esse método resultarão nos threads aguardando acesso a um objeto que nunca será retornado, porque o thread que está usando o objeto bloqueado está morto.

Eu acho que entendi tudo, mas se eu estiver fora da base, por favor me avise. O conceito fez sentido para mim. Eu não estava completamente convencido de que esse era um cenário real que poderia acontecer no .NET. Eu nunca ouvi falar de "zumbis", mas reconheço que os programadores que trabalharam em profundidade em níveis mais baixos tendem a ter uma compreensão mais profunda dos fundamentos da computação (como o threading). Definitivamente, vejo o valor do bloqueio, e já vi muitos programadores de classe mundial alavancarem o bloqueio. Também tenho capacidade limitada de avaliar isso por mim mesmo, porque sei que a lock(obj)afirmação é realmente apenas um açúcar sintático para:

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

e porque Monitor.Entere Monitor.Exitsão marcados extern. Parece concebível que o .NET faça algum tipo de processamento que proteja os threads da exposição a componentes do sistema que possam ter esse tipo de impacto, mas isso é puramente especulativo e provavelmente apenas com base no fato de que eu nunca ouvi falar de "threads de zumbis" antes. Então, espero obter algum feedback sobre isso aqui:

  1. Existe uma definição mais clara de um "fio de zumbi" do que eu expliquei aqui?
  2. Threads zumbis podem ocorrer no .NET? (Porque porque não?)
  3. Se aplicável, como forçar a criação de um thread zumbi no .NET?
  4. Se aplicável, como posso aproveitar o bloqueio sem arriscar um cenário de encadeamento de zumbis no .NET?

Atualizar

Eu fiz essa pergunta há pouco mais de dois anos. Hoje isso aconteceu:

O objeto está em um estado de zumbi.

smartcaveman
fonte
8
tem certeza de que seu colega não fala de impasse?
Andreas Niedermair
10
@AndreasNiedermair - Eu sei o que é um impasse e claramente não foi uma questão de uso indevido dessa terminologia. O impasse foi mencionado na conversa e era claramente distinto de um "fio de zumbi". Para mim, a principal distinção é que um bloqueio morto possui uma dependência bidirecional não resolvível, enquanto o thread zombie é unidirecional e requer um processo finalizado. Se você discorda e acha que há uma maneira melhor de analisar essas coisas, por favor, explique
smartcaveman
19
Eu acho que o termo "zumbi" realmente vem de um fundo UNIX, como em "processo de zumbi", certo ??? Há uma definição clara de um "processo zumbi" no UNIX: descreve um processo filho que foi encerrado, mas onde o pai do processo filho ainda precisa "liberar" o processo filho (e seus recursos) chamando waitou waitpid. O processo filho é então chamado de "processo zumbi". Veja também howtogeek.com/119815
hogliux
9
Se parte do seu programa travar, deixando-o em um estado indefinido, é claro que isso pode causar problemas com o restante do programa. O mesmo pode acontecer se você manipular incorretamente exceções em um programa de thread único. O problema não está nos encadeamentos, o problema é que você possui um estado mutável global e não está lidando adequadamente com o encerramento inesperado de encadeamentos. Seu colega de trabalho "realmente brilhante" está totalmente fora de base nesse caso.
Jim Mischel
9
"Desde os primeiros computadores, sempre houve fantasmas na máquina. Segmentos aleatórios de código que foram agrupados para formar protocolos inesperados ..."
Chris Laplante

Respostas:

242
  • Existe uma definição mais clara de um "fio de zumbi" do que eu expliquei aqui?

Parece uma boa explicação para mim - um thread que foi encerrado (e, portanto, não pode mais liberar nenhum recurso), mas cujos recursos (por exemplo, identificadores) ainda estão por aí e (potencialmente) causando problemas.

  • Threads zumbis podem ocorrer no .NET? (Porque porque não?)
  • Se aplicável, como forçar a criação de um thread zumbi no .NET?

Eles certamente fazem, olha, eu fiz um!

[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 um thread Targetque abre um arquivo e depois se mata imediatamente usando ExitThread. O thread zumbi resultante nunca libera o identificador para o arquivo "test.txt" e, portanto, o arquivo permanece aberto até o término do programa (você pode verificar com o Process Explorer ou similar). O identificador para "test.txt" não será liberado até que GC.Collectseja chamado - acontece que é ainda mais difícil do que eu pensava em criar um thread de zumbi que vazava identificadores)

  • Se aplicável, como posso aproveitar o bloqueio sem arriscar um cenário de encadeamento de zumbis no .NET?

Não faça o que acabei de fazer!

Desde que o seu código seja limpo corretamente (use Safe Handles ou classes equivalentes se estiver trabalhando com recursos não gerenciados) e contanto que você não se esforce para eliminar threads de maneiras estranhas e maravilhosas (a maneira mais segura é apenas para nunca matar threads - deixe que eles terminem normalmente, ou através de exceções, se necessário), a única maneira de obter algo parecido com um thread de zumbi é se algo der muito errado (por exemplo, algo der errado no CLR).

De fato, é surpreendentemente difícil criar um encadeamento zumbi (eu tive que P / Invoke em uma função que essencialmente diz a você na documentação para não chamá-lo fora de C). Por exemplo, o seguinte código (terrível) na verdade não cria um thread de zumbi.

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);
}

Apesar de cometer alguns erros terríveis, o identificador para "test.txt" ainda é fechado assim que Aborté chamado (como parte do finalizador para o filequal, sob as capas, usa SafeFileHandle para quebrar seu identificador de arquivo)

O exemplo de bloqueio na resposta C.Evenhuis é provavelmente a maneira mais fácil de deixar de liberar um recurso (um bloqueio nesse caso) quando um encadeamento é encerrado de uma maneira não estranha, mas isso é facilmente corrigido usando uma lockinstrução ou colocando o lançamento em um finallybloco.

Veja também

Justin
fonte
3
Lembro-me de quando joguei salvando coisas no Excel usando um backgroundworker. Eu não liberava todos os recursos o tempo todo (porque eu simplesmente ignorei a depuração etc.). no taskmanager, vi depois cerca de 50 processos do excel. eu criei zombieexcelprocesses?
19413 Stefan
3
@ Justin - +1 - Ótima resposta. Eu sou um pouco cético em relação à sua ExitThreadligação. Obviamente, funciona, mas parece mais um truque inteligente do que um cenário realista. Um dos meus objetivos é aprender o que não fazer para não criar acidentalmente threads de zumbi com código .NET. Eu provavelmente poderia ter descoberto que chamar o código C ++ que causa esse problema no código .NET produziria o efeito desejado. Você obviamente sabe muito sobre essas coisas. Você conhece outros casos (possivelmente estranhos, mas não estranhos o suficiente para nunca acontecer acidentalmente) com o mesmo resultado?
smartcaveman
3
Então a resposta é 'não está em c # 4', certo? Se você precisar sair do CLR para obter um thread de zumbi, isso não parece um problema .Net.
Gusdor #
4
@ Stefan eu diria quase que definitivamente não. Eu fiz o que você descreveu várias vezes. Os processos do Excel não são zumbis, pois ainda estão em execução, embora não sejam acessíveis imediatamente pela interface normal. Você poderá recuperá-los por meio de chamadas para GetObject . Set excelInstance = GetObject(, "Excel.Application")
Daniel
7
"O thread zombie resultante nunca libera o identificador para o arquivo" test.txt "e, portanto, o arquivo permanecerá aberto até o término do programa" está incorreto. Prova pequena: `static void Main (string [] args) {new Thread (GcCollect) .Start (); novo Thread (Target) .Start (); Console.ReadLine (); } void estático privado Target () {using (var file = File.Open ("test.txt", FileMode.OpenOrCreate))) {ExitThread (0); }} private static void GcCollect () {while (true) {Thread.Sleep (10000); GC.Collect (); }} `
Sinix
46

Limpei um pouco minha resposta, mas deixei a original abaixo para referência

É a primeira vez que ouvi falar do termo zumbis, então assumirei que sua definição é:

Um encadeamento que foi finalizado sem liberar todos os seus recursos

Portanto, dada essa definição, sim, você pode fazer isso no .NET, como em outras linguagens (C / C ++, java).

No entanto , não considero isso um bom motivo para não escrever código de missão crítica encadeado no .NET. Pode haver outros motivos para decidir contra o .NET, mas anular o .NET apenas porque você pode ter threads de zumbi de alguma forma não faz sentido para mim. Threads zumbis são possíveis em C / C ++ (eu até diria que é muito mais fácil mexer em C) e muitos aplicativos encadeados críticos estão em C / C ++ (comércio de alto volume, bancos de dados etc.).

Conclusão Se você está decidindo sobre um idioma a usar, sugiro que você considere o cenário geral: desempenho, habilidades de equipe, cronograma, integração com aplicativos existentes, etc. , mas como é muito difícil cometer esse erro no .NET em comparação com outros idiomas como C, acho que essa preocupação será ofuscada por outras coisas, como as mencionadas acima. Boa sorte!

Resposta original Zumbis podem existir se você não escrever um código de encadeamento adequado. O mesmo vale para outras linguagens como C / C ++ e Java. Mas esse não é um motivo para não escrever código encadeado no .NET.

E, como em qualquer outro idioma, saiba o preço antes de usar algo. Também ajuda a saber o que está acontecendo sob o capô, para que você possa prever possíveis problemas.

Código confiável para sistemas de missão crítica não é fácil de escrever, seja qual for o idioma em que você esteja. Mas tenho certeza de que não é impossível fazer corretamente no .NET. Além disso, o AFAIK, .NET threading não é tão diferente do threading no C / C ++, ele usa (ou é construído a partir) das mesmas chamadas de sistema, exceto para algumas construções específicas do .net (como as versões leves de RWL e classes de eventos).

primeira vez que ouvi falar do termo zumbis, mas com base na sua descrição, seu colega provavelmente quis dizer um thread que terminou sem liberar todos os recursos. Isso pode causar um conflito, vazamento de memória ou algum outro efeito colateral ruim. Obviamente, isso não é desejável, mas destacar o .NET por causa dessa possibilidade provavelmente não é uma boa ideia, pois também é possível em outros idiomas. Eu diria até que é mais fácil bagunçar em C / C ++ do que em .NET (especialmente em C onde você não possui RAII), mas muitos aplicativos críticos são escritos em C / C ++, certo? Então, isso realmente depende das suas circunstâncias individuais. Se você deseja extrair toda a velocidade do seu aplicativo e deseja chegar o mais próximo possível do bare metal, o .NET podenão ser a melhor solução. Se você tem um orçamento apertado e faz muita interface com serviços web / bibliotecas .net existentes / etc, o .NET pode ser uma boa escolha.

Jerahmeel
fonte
(1) Não tenho certeza de como descobrir o que está acontecendo sob o capô quando eu chego aos externmétodos sem saída . Se você tem uma sugestão, eu adoraria ouvi-la. (2) Concordo que é possível fazer no .NET. Gostaria de acreditar que é possível com bloqueio, mas eu ainda tenho que encontrar uma resposta satisfatória para justificar que hoje
smartcaveman
11
@smartcaveman se o que você quer dizer com palavra locking- lockchave, provavelmente não, pois ela serializa a execução. Para maximizar a taxa de transferência, você precisará usar as construções corretas, dependendo das características do seu código. Eu diria que se você pode escrever código confiável em c / c ++ / java / qualquer que seja o uso de pthreads / boost / thread pools / o que for, então você pode escrevê-lo em C # também. Mas se você não pode escrever código confiável em qualquer idioma usando qualquer biblioteca, duvido que escrever em C # seja diferente.
precisa
@smartcaveman quanto a descobrir o que está por trás, o Google ajuda os montões e, se o problema é exótico demais para encontrar na web, o refletor é muito útil. Mas para as classes de encadeamento, acho a documentação do MSDN muito útil. E muitos são apenas invólucros para as mesmas chamadas de sistema que você usa no C anway.
precisa
11
@smartcaveman de jeito nenhum, não foi isso que eu quis dizer. Me desculpe se isso aconteceu dessa maneira. O que eu queria dizer é que você não deve ser muito rápido para anular o .NET ao escrever um aplicativo crítico. Claro, você pode fazer coisas que são muito piores do que os threads zumbis (que eu acho que são apenas threads que não liberam recursos não gerenciados, o que pode acontecer totalmente em outros idiomas: stackoverflow.com/questions/14268080/… ), mas Novamente, isso não significa que o .NET não seja uma solução viável.
precisa
2
Eu não acho que o .NET seja uma tecnologia ruim. Na verdade, é minha principal estrutura de desenvolvimento. Mas, por isso, acho importante entender as falhas às quais é suscetível. Basicamente, estou destacando o .NET, mas porque gosto, não porque não. (E não se preocupe bro, I + 1-ed você)
smartcaveman
26

No momento, a maior parte da minha resposta foi corrigida pelos comentários abaixo. Não excluirei a resposta porque preciso dos pontos de reputação, pois as informações nos comentários podem ser valiosas para os leitores.

O Immortal Blue apontou que no .NET 2.0 e acima os finallyblocos são imunes a interrupções de threads. E, como comentado por Andreas Niedermair, esse pode não ser um thread zumbi real, mas o exemplo a seguir mostra como o cancelamento de um thread pode 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");
    }
}

No entanto, ao usar um lock() { }bloco, finallyele ainda será executado quando a ThreadAbortExceptionfor disparado dessa maneira.

As informações a seguir, como se vê, são válidas apenas para .NET 1 e .NET 1.1:

Se dentro do lock() { }bloco ocorrer outra exceção e ela ThreadAbortExceptionchegar exatamente quando o finallybloco estiver prestes a ser executado, o bloqueio não será liberado. Como você mencionou, o lock() { }bloco é compilado como:

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

Se outro encadeamento chamar Thread.Abort()dentro do finallybloco gerado , o bloqueio poderá não ser liberado.

C.Evenhuis
fonte
2
você está falando, lock()mas não consigo ver nenhum uso disso ... então - como isso está conectado? este é um uso "errado" Monitor.Entere Monitor.Exit(sem o uso de trye finally)
Andreas Niedermair
4
Eu não chamaria isso de thread de zumbi - este é simplesmente um uso incorreto Monitor.Entere Monitor.Exitsem o uso adequado de - trye finally, de qualquer maneira, seu cenário trava outros threads que podem se agarrar _lock, então há um cenário de impasse - não necessariamente um thread de zumbi ... Além disso, você não está liberando o bloqueio em Main... mas, hey ... talvez o OP está travando para supertrancamento vez de zumbi tópicos :)
Andreas Niedermair
11
@AndreasNiedermair talvez a definição de um tópico de zumbi não seja exatamente o que eu pensava. Talvez possamos chamar isso de "thread que terminou a execução, mas não liberou todos os recursos". Tentou excluir e fazer minha lição de casa, mas mantendo a resposta para a semelhança com o cenário do OP.
C.Evenhuis
2
sem ofensa aqui! na verdade, sua resposta me fez pensar sobre isso :) como o OP está explicitamente falando sobre bloqueios não sendo liberados, acredito que seu colega falou sobre bloqueio morto - porque um bloqueio não é um recurso real vinculado a um thread ( ela é compartilhada ... Nona) - e, portanto, qualquer tópico "zombie" poderia ser evitado aderindo a melhores práticas e utilizando lockou adequada try/ finally-usage
Andreas Niedermair
11
@ C.Evenhuis - Não tenho certeza de que minha definição seja precisa. Parte do motivo pelo qual estou fazendo a pergunta é esclarecer isso. Eu acho que o conceito é referido pesadamente em C / C ++
smartcaveman
24

Não se trata de tópicos do Zombie, mas o livro Effective C # tem uma seção sobre a implementação do IDisposable, (item 17), que fala sobre objetos do Zombie que eu achei que você poderia achar interessantes.

Eu recomendo a leitura do livro, mas o essencial é que, se você tem uma classe implementando IDisposable ou contendo um Desctructor, a única coisa que você deve fazer é liberar recursos. Se você fizer outras coisas aqui, há uma chance de o objeto não ser coletado como lixo, mas também não estará acessível de forma alguma.

Ele fornece um exemplo semelhante ao abaixo:

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

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

Quando o destruidor desse objeto é chamado, uma referência a si mesma é colocada na lista global, o que significa que ele permanece vivo e na memória durante toda a vida útil do programa, mas não está acessível. Isso pode significar que os recursos (principalmente os recursos não gerenciados) podem não ser totalmente liberados, o que pode causar todo tipo de problema em potencial.

Um exemplo mais completo está abaixo. No momento em que o loop foreach é atingido, você tem 150 objetos na lista de Mortos-Vivos, cada um contendo uma imagem, mas a imagem foi gerada em GC e você recebe uma exceção se tentar usá-la. Neste exemplo, estou recebendo uma ArgumentException (Parameter não é válido) quando tento fazer alguma coisa com a imagem, tente salvá-la ou até mesmo exibir dimensões como altura e largura:

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);
    }
}

Novamente, eu sei que você estava perguntando sobre tópicos de zumbis em particular, mas o título da pergunta é sobre zumbis em .net, e eu me lembrei disso e pensei que outros poderiam achar interessante!

JMK
fonte
11
Isto é interessante. É _undeadpara ser estático?
smartcaveman
11
Então, eu tentei, com algumas impressões do objeto "destruído". Trata-o como um objeto normal. Existe algo problemático em fazer isso?
smartcaveman
11
Atualizei minha resposta com um exemplo que espero demonstre a questão um pouco mais claramente.
JMK
11
Não sei por que você foi derrotado. Achei útil. Aqui está uma pergunta - que tipo de exceção você receberá? Não espero que seja um NullReferenceException, porque sinto que a coisa que falta falta está mais ligada à máquina do que à aplicação. Isto está certo?
smartcaveman
4
Certamente não é tanto IDisposableesse o problema. Recebo a preocupação com os finalizadores (destruidores), mas simplesmente IDisposablenão fazer com que um objeto vá para a fila do finalizador e arrisque esse cenário de zumbi. Este aviso diz respeito aos finalizadores e eles podem chamar métodos Dispose. Existem exemplos em que IDisposableé usado em tipos sem finalizadores. O sentimento deve ser a limpeza de recursos, mas isso pode ser uma limpeza não trivial de recursos. O RX usa validamente IDisposablepara limpar as assinaturas e pode chamar outros recursos a jusante. (Eu não downvote quer btw ...)
James Mundial
21

Em sistemas críticos sob carga pesada, escrever código sem bloqueio é melhor principalmente por causa das melhorias no desempenho. Veja coisas como o LMAX e como ele aproveita a "simpatia mecânica" para grandes discussões sobre isso. Se preocupe com tópicos de zumbis? Eu acho que é um caso de ponta que é apenas um bug a ser resolvido, e não é uma razão boa o suficiente para não usarlock .

Parece mais que seu amigo está apenas sendo chique e exibindo seu conhecimento de terminologia exótica obscura para mim! Durante todo o tempo em que eu estava executando os laboratórios de desempenho na Microsoft UK, nunca me deparei com uma instância desse problema no .NET.

James World
fonte
2
Eu acho que diferentes conjuntos de experiências nos tornam paranóicos em relação a diferentes bugs. Ele reconheceu que é um caso extremo ao explicá-lo, mas se é realmente um caso extremo e não um caso nunca, eu gostaria de pelo menos entender um pouco melhor - Obrigado pela sua
opinião
11
Justo. Eu só não gostaria que você estivesse desproporcionalmente preocupado com a lockafirmação!
James World
11
Eu ficaria muito menos preocupado se tivesse um entendimento mais profundo desse problema.
smartcaveman
4
Eu votei porque você está tentando se livrar de algum FUD de discussão, do qual há muito. O Lock-FUD precisará de mais trabalho para se desfazer :) Eu me encolho quando os desenvolvedores usam um spinlock ilimitado (para desempenho), para que possam copiar 50K de dados em uma fila larga.
Martin James
3
Sim, tanto no geral como no particular. Escrever código sem bloqueio correto é quase tão desafiador quanto a programação. De acordo com a minha experiência na grande maioria dos casos, lockblocos grandes e grainizados (relativamente) de fácil compreensão são muito bons. Eu evitaria introduzir a complexidade por uma questão de otimização de desempenho até que você saiba que precisa.
James Mundial
3

1. Existe uma definição mais clara de um "fio zumbi" do que o que expliquei aqui?

Concordo que "Zombie Threads" existe, é um termo para se referir ao que acontece com Threads que são deixados com recursos que eles não soltam e ainda não morrem completamente, daí o nome "zombie". A explicação dessa referência está certa no dinheiro!

2.Os threads de zumbi podem ocorrer no .NET? (Porque porque não?)

Sim, eles podem ocorrer. É uma referência e, na verdade, referida pelo Windows como "zumbi": o MSDN usa a palavra "zumbi" para processos / threads inoperantes

Acontecer com frequência é outra história, e depende de suas técnicas e práticas de codificação, pois para você que gosta de Thread Locking e faz isso há algum tempo, eu nem me preocupo com esse cenário.

E sim, como o @KevinPanko mencionou corretamente nos comentários, "Zombie Threads" vem do Unix, e é por isso que eles são usados ​​no XCode-ObjectiveC e chamados de "NSZombie" e usados ​​para depuração. Ele se comporta da mesma maneira ... a única diferença é que um objeto que deveria ter morrido se torna um "ZombieObject" para depuração, em vez do "Zombie Thread", que pode ser um problema em potencial no seu código.

SH
fonte
11
Mas a maneira como o MSDN usa o zumbi é muito diferente da maneira como esta pergunta está sendo usada.
precisa
11
Ah, sim, eu concordo, mas eu estava afirmando que até o MSDN se refere a threads como Zombie Threads quando está morto. e que eles de fato podem ocorrer.
SH
4
Claro, mas está se referindo a algum outro código que ainda mantém um identificador no segmento, e não no segmento que mantém identificadores de recursos quando ele saiu. Sua primeira frase, concordando com a definição da pergunta, é o que está errado.
Ben Voigt
11
Ahh, entendo o seu ponto. Para ser honesto, eu nem percebi isso. Eu estava focando no argumento dele de que a definição existe, independentemente de como isso acontece. Lembre-se de que a definição existe por causa do que acontece com o encadeamento, e não como é feita.
SH
0

Eu posso fazer threads de zumbi com bastante facilidade.

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

Isso vaza as alças de thread (para Join()). É apenas mais um vazamento de memória no que diz respeito ao mundo gerenciado.

Agora, matar um fio de uma maneira que realmente trava é uma dor na parte traseira, mas possível. O outro cara ExitThread()faz o trabalho. Como ele descobriu, o identificador de arquivo foi limpo pelo GC, mas um lockobjeto em volta não. Mas por que você faria isso?

Joshua
fonte