Gli zombi esistono ... in .NET?


385

Stavo discutendo con un compagno di squadra sul blocco in .NET. È un ragazzo davvero brillante con una vasta esperienza sia nella programmazione di livello inferiore che di livello superiore, ma la sua esperienza con la programmazione di livello inferiore supera di gran lunga la mia. Ad ogni modo, ha sostenuto che il blocco di .NET dovrebbe essere evitato su sistemi critici che dovrebbero essere sottoposti a carichi pesanti, per quanto possibile, al fine di evitare la piccola possibilità di un "thread zombi" che si schianta contro un sistema. Uso abitualmente il blocco e non sapevo cosa fosse un "filo zombi", quindi ho chiesto. L'impressione che ho avuto dalla sua spiegazione è che un thread di zombi è un thread che è terminato ma che in qualche modo mantiene ancora alcune risorse. Un esempio di come un thread di zombi potrebbe rompere un sistema è stato un thread che inizia una procedura dopo aver bloccato un oggetto, e poi viene a un certo punto terminato prima che il blocco possa essere rilasciato. Questa situazione ha il potenziale per arrestare il sistema in modo anomalo, perché alla fine, i tentativi di eseguire quel metodo comporteranno tutti i thread in attesa dell'accesso a un oggetto che non verrà mai restituito, perché il thread che utilizza l'oggetto bloccato è morto.

Penso di averne capito il senso, ma se sono fuori base, per favore fatemelo sapere. Il concetto aveva senso per me. Non ero del tutto convinto che questo fosse uno scenario reale che poteva accadere in .NET. Non ho mai sentito parlare di "zombi", ma riconosco che i programmatori che hanno lavorato in modo approfondito ai livelli più bassi tendono ad avere una comprensione più profonda dei fondamenti dell'informatica (come il threading). Sicuramente vedo il valore del blocco, tuttavia, e ho visto molti programmatori di livello mondiale sfruttare il blocco. Ho anche una capacità limitata di valutarlo da solo perché so che l' lock(obj)affermazione è davvero solo zucchero sintattico per:

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

e perché Monitor.Entere Monitor.Exitsono contrassegnati extern. Sembra ipotizzabile che .NET esegua un qualche tipo di elaborazione che protegge i thread dall'esposizione a componenti di sistema che potrebbero avere questo tipo di impatto, ma che è puramente speculativo e probabilmente basato solo sul fatto che non ho mai sentito parlare di "thread zombi" prima. Quindi, spero di poter ottenere un feedback su questo qui:

  1. Esiste una definizione più chiara di "filo zombi" rispetto a quella che ho spiegato qui?
  2. I thread di zombi possono verificarsi su .NET? (Perché perché no?)
  3. Se applicabile, come potrei forzare la creazione di un thread zombi in .NET?
  4. Se applicabile, come posso sfruttare il blocco senza rischiare uno scenario di thread zombi in .NET?

Aggiornare

Ho fatto questa domanda poco più di due anni fa. Oggi è successo:

L'oggetto è in uno stato zombi.


8
sei sicuro che il tuo compagno non parli di deadlock?
Andreas Niedermair,

10
@AndreasNiedermair - So cos'è il deadlock e chiaramente non si trattava di un uso improprio di quella terminologia. Il deadlock è stato menzionato nella conversazione ed era chiaramente distinto da un "filo di zombi". Per me, la distinzione principale è che un dead lock ha una dipendenza non risolvibile a due vie mentre il thread di zombi è a senso unico e richiede un processo terminato. Se non sei d'accordo e pensi che ci sia un modo migliore di guardare a queste cose, ti preghiamo di spiegare
smartcaveman,

19
Penso che il termine "zombi" in realtà provenga da un backgound UNIX, come in "processo di zombi", giusto ??? Esiste una chiara definizione di "processo zombi" in UNIX: descrive un processo figlio che è terminato ma in cui il genitore del processo figlio deve ancora "rilasciare" il processo figlio (e le sue risorse) chiamando waito waitpid. Il processo figlio viene quindi chiamato "processo zombi". Vedi anche howtogeek.com/119815
hogliux

9
Se parte del programma si arresta in modo anomalo, lasciando il programma in uno stato indefinito, ovviamente ciò può causare problemi con il resto del programma. La stessa cosa può succedere se gestisci in modo improprio le eccezioni in un programma a thread singolo. Il problema non riguarda i thread, il problema è che hai uno stato mutabile globale e che non stai gestendo correttamente la terminazione imprevista del thread. Il tuo collega "veramente brillante" è totalmente fuori base su questo.
Jim Mischel,

9
"Sin dai primi computer, ci sono sempre stati fantasmi nella macchina. Segmenti casuali di codice che si sono raggruppati per formare protocolli inaspettati ..."
Chris Laplante,

Risposte:


242
  • Esiste una definizione più chiara di "filo zombi" rispetto a quella che ho spiegato qui?

Mi sembra una spiegazione abbastanza buona per me - un thread che è terminato (e quindi non può più rilasciare alcuna risorsa), ma le cui risorse (ad esempio handle) sono ancora in giro e (potenzialmente) causando problemi.

  • I thread di zombi possono verificarsi su .NET? (Perché perché no?)
  • Se applicabile, come potrei forzare la creazione di un thread zombi in .NET?

Sicuramente lo fanno, senti, ne ho creato 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);
    }
}

Questo programma avvia un thread Targetche apre un file e poi si uccide immediatamente usando ExitThread. Il thread zombie risultante non rilascerà mai l'handle nel file "test.txt" e quindi il file rimarrà aperto fino al termine del programma (è possibile verificare con Process Explorer o simili). L'handle di "test.txt" non verrà rilasciato fino a quando non GC.Collectviene chiamato - si scopre che è ancora più difficile di quanto pensassi di creare un thread di zombi che perde maniglie)

  • Se applicabile, come posso sfruttare il blocco senza rischiare uno scenario di thread zombi in .NET?

Non fare quello che ho appena fatto!

Finché il codice viene ripulito correttamente dopo se stesso (utilizzare Maniglie sicure o classi equivalenti se si lavora con risorse non gestite) e finché non si fa di tutto per uccidere i thread in modi strani e meravigliosi (il modo più sicuro è solo per non uccidere mai i thread - lasciateli terminare normalmente, o attraverso eccezioni se necessario), l'unico modo per avere qualcosa che assomigli a un thread di zombi è se qualcosa è andato molto storto (ad esempio qualcosa va storto nel CLR).

In realtà è sorprendentemente difficile creare un thread di zombi (ho dovuto P / Invocare in una funzione che ti dice in modo essenziale nella documentazione di non chiamarlo al di fuori di C). Ad esempio il seguente codice (terribile) in realtà non crea un thread di zombi.

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

Nonostante abbia commesso degli errori terribili, l'handle di "test.txt" è ancora chiuso non appena Abortviene chiamato (come parte del finalizzatore per il filequale sotto le copertine utilizza SafeFileHandle per avvolgere l'handle del file)

L'esempio di blocco nella risposta di C.Evenhuis è probabilmente il modo più semplice per non rilasciare una risorsa (un blocco in questo caso) quando un thread viene terminato in un modo non strano, ma questo può essere risolto facilmente usando lockinvece un'istruzione, oppure mettendo il finallyblocco in un blocco.

Guarda anche


3
Ricordo che quando giocavo con il salvataggio di roba in Excel usando un backgroundworker non rilasciavo tutte le risorse per tutto il tempo (perché ho appena saltato il debugging ecc.). nel taskmanager ho visto in seguito circa 50 processi eccellenti. ho creato zombieexcelprocesses?
Stefan

3
@Justin - +1 - Ottima risposta. Sono un po 'scettico sulla tua ExitThreadchiamata però. Ovviamente funziona, ma sembra più un trucco intelligente che uno scenario realistico. Uno dei miei obiettivi è quello di imparare cosa non fare in modo da non creare accidentalmente thread zombi con il codice .NET. Probabilmente avrei potuto capire che chiamare il codice C ++ che è noto per causare quel problema dal codice .NET avrebbe prodotto l'effetto desiderato. Ovviamente sai molto su questa roba. Conosci altri casi (forse strani, ma non abbastanza strani da non accadere mai involontariamente) con lo stesso risultato?
smartcaveman,

3
Quindi la risposta è 'non in c # 4', giusto? Se devi saltare fuori dal CLR per ottenere un thread di zombi, non sembra un problema .Net.
Gusdor,

4
@Stefan direi quasi sicuramente no. Ho fatto quello che hai descritto molte volte. I processi di Excel non sono zombie in quanto sono ancora in esecuzione anche se non immediatamente accessibili tramite la normale interfaccia utente. Dovresti essere in grado di recuperarli tramite chiamate a GetObject . Set excelInstance = GetObject(, "Excel.Application")
Daniel,

7
"Il thread zombie risultante non rilascerà mai l'handle nel file" test.txt "e quindi il file rimarrà aperto fino al termine del programma" non è corretto. Piccola dimostrazione: `static void Main (string [] args) {new Thread (GcCollect) .Start (); new Thread (Target) .Start (); Console.ReadLine (); } void statico privato Target () {using (var file = File.Open ("test.txt", FileMode.OpenOrCreate)) {ExitThread (0); }} vuoto statico privato GcCollect () {while (true) {Thread.Sleep (10000); GC.Collect (); }} `
Sinix,

46

Ho ripulito un po 'la mia risposta, ma ho lasciato quello originale di seguito come riferimento

È la prima volta che sento parlare del termine zombi, quindi suppongo che la sua definizione sia:

Un thread che è terminato senza rilasciare tutte le sue risorse

Quindi, data questa definizione, quindi sì, puoi farlo in .NET, come con altri linguaggi (C / C ++, java).

Tuttavia , non penso che questo sia un buon motivo per non scrivere un codice thread-mission-critical in .NET. Potrebbero esserci altri motivi per decidere contro .NET, ma cancellarlo solo perché in qualche modo puoi avere thread di zombi non ha senso per me. I thread di zombi sono possibili in C / C ++ (direi anche che è molto più facile sbagliare in C) e molte app critiche e thread sono in C / C ++ (trading ad alto volume, database ecc.).

Conclusione Se stai per decidere una lingua da usare, ti suggerisco di prendere in considerazione il quadro generale: prestazioni, abilità di squadra, pianificazione, integrazione con app esistenti ecc. Certo, i thread di zombi sono qualcosa a cui dovresti pensare , ma dal momento che è davvero difficile fare questo errore in .NET rispetto ad altri linguaggi come C, penso che questa preoccupazione sarà oscurata da altre cose come quelle sopra menzionate. In bocca al lupo!

La risposta originale Zombies può esistere se non scrivi il codice di threading corretto. Lo stesso vale per altri linguaggi come C / C ++ e Java. Ma questo non è un motivo per non scrivere codice threaded in .NET.

E proprio come con qualsiasi altra lingua, conoscere il prezzo prima di utilizzare qualcosa. Aiuta anche a sapere cosa sta succedendo sotto il cofano in modo da poter prevedere eventuali problemi.

Il codice affidabile per i sistemi mission critical non è facile da scrivere, qualunque sia la lingua in cui ti trovi. Ma sono certo che non sia impossibile farlo correttamente in .NET. Anche AFAIK, il threading .NET non è così diverso dal threading in C / C ++, utilizza (o è costruito da) le stesse chiamate di sistema ad eccezione di alcuni costrutti specifici .net (come le versioni leggere di RWL e le classi di eventi).

† la prima volta che ho sentito parlare del termine zombi, ma in base alla tua descrizione, il tuo collega probabilmente intendeva un thread terminato senza rilasciare tutte le risorse. Ciò potrebbe potenzialmente causare un deadlock, una perdita di memoria o qualche altro effetto collaterale negativo. Questo ovviamente non è desiderabile, ma individuare .NET a causa di questa possibilità non è probabilmente una buona idea poiché è possibile anche in altre lingue. Direi anche che è più facile sbagliare in C / C ++ che in .NET (specialmente in C dove non hai RAII) ma molte app critiche sono scritte in C / C ++ giusto? Quindi dipende davvero dalle tue circostanze individuali. Se vuoi estrarre ogni oncia di velocità dalla tua applicazione e vuoi avvicinarti il ​​più possibile al bare metal, .NET potrebbenon essere la soluzione migliore. Se hai un budget limitato e fai molte interfacce con i servizi web / le librerie .net esistenti / ecc. NET potrebbe essere una buona scelta.


(1) Non sono sicuro di come capire cosa sta succedendo sotto il cofano quando colpisco externmetodi senza uscita . Se hai un suggerimento, mi piacerebbe ascoltarlo. (2) Concordo sul fatto che è possibile farlo in .NET. Vorrei credere che sia possibile con il blocco, ma devo ancora trovare una risposta soddisfacente per giustificarlo oggi
smartcaveman,

1
@smartcaveman se intendi lockingla lockparola chiave, probabilmente non perché serializza l'esecuzione. Per massimizzare il rendimento, dovresti usare i costrutti giusti a seconda delle caratteristiche del tuo codice. Direi che se puoi scrivere un codice affidabile in c / c ++ / java / qualunque cosa tu usi pthreads / boost / thread pool / qualunque cosa, allora puoi scriverlo anche in C #. Ma se non riesci a scrivere codice affidabile in nessuna lingua usando una libreria, dubito che scrivere in C # sarà diverso.
Jerahmeel,

@smartcaveman come per capire cosa c'è sotto il cofano, google aiuta un sacco e se il tuo problema è troppo esotico per trovarlo sul web, il riflettore è molto utile. Ma per le classi di threading, trovo molto utile la documentazione MSDN. E molti di questi sono solo wrapper per le stesse chiamate di sistema che usi in C anway.
Jerahmeel,

1
@smartcaveman per niente, non intendevo questo. Mi dispiace se si è imbattuto in quel modo. Quello che volevo dire è che non dovresti essere troppo veloce per cancellare .NET quando scrivi un'applicazione critica. Certo, potresti fare cose che forse sono molto peggio dei thread di zombi (che penso siano solo thread che non hanno rilasciato risorse non gestite, cosa che può accadere totalmente in altre lingue: stackoverflow.com/questions/14268080/… ), ma di nuovo, ciò non significa che .NET non sia una soluzione praticabile.
Jerahmeel,

2
Non credo che .NET sia una cattiva tecnologia. In realtà è il mio quadro di sviluppo primario. Ma, per questo motivo, penso che sia importante capire i difetti a cui è suscettibile. Fondamentalmente, sto individuando .NET, ma perché mi piace, non perché non mi piaccia. (E non preoccuparti fratello, ti ho fatto +1)
smartcaveman,

26

In questo momento la maggior parte della mia risposta è stata corretta dai commenti qui sotto. Non eliminerò la risposta perché ho bisogno dei punti reputazione perché le informazioni nei commenti possono essere preziose per i lettori.

Immortal Blue ha sottolineato che in .NET 2.0 e versioni successive i finallyblocchi sono immuni alle interruzioni del thread. E come commentato da Andreas Niedermair, questo potrebbe non essere un vero thread di zombi, ma il seguente esempio mostra come interrompere un thread può causare problemi:

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

Tuttavia, quando si utilizza un lock() { }blocco, finallyverrà comunque eseguito quando un ThreadAbortExceptionviene sparato in quel modo.

Le seguenti informazioni, a quanto pare, sono valide solo per .NET 1 e .NET 1.1:

Se all'interno del lock() { }blocco si verifica un'altra eccezione e ThreadAbortExceptionarriva esattamente quando il finallyblocco sta per essere eseguito, il blocco non viene rilasciato. Come hai detto, il lock() { }blocco è compilato come:

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

Se un altro thread chiama Thread.Abort()all'interno del finallyblocco generato , il blocco potrebbe non essere rilasciato.


2
stai parlando lock()ma non riesco a vedere alcun uso di questo ... quindi - come è collegato? questo è un uso "sbagliato" di Monitor.Entere Monitor.Exit(manca l'utilizzo di trye finally)
Andreas Niedermair

4
Non lo definirei un thread di zombi - questo è semplicemente un uso errato di Monitor.Entere Monitor.Exitsenza un uso corretto ditry e finally- comunque, il tuo scenario bloccherà altri thread che potrebbero aggrapparsi _lock, quindi c'è uno scenario di deadlock - non necessariamente un thread di zombi ... Inoltre, non stai rilasciando il blocco in Main... ma, ehi ... forse l'OP sta bloccando per deadlock invece di thread di zombi :)
Andreas Niedermair,

1
@AndreasNiedermair forse la definizione di un filo zombi non è esattamente quello che pensavo. Forse possiamo definirlo un "thread che ha terminato l'esecuzione ma non ha rilasciato tutte le risorse". Sono tentato di cancellare e fare i compiti, ma mantenendo la risposta per la somiglianza con lo scenario del PO.
C.Evenhuis,

2
senza offesa qui! in realtà la tua risposta mi ha appena fatto pensare :) poiché l'OP sta esplicitamente parlando di blocchi non rilasciati, credo che il suo compagno abbia parlato di dead-lock - perché un lock non è una vera risorsa legata a un thread ( è condiviso ... nöna) - e quindi qualsiasi thread "zombi" potrebbe essere evitato attenendosi alle migliori pratiche e usando lockotry / / finallyusage
Andreas Niedermair,

1
@ C.Evenhuis - Non sono sicuro che la mia definizione sia accurata. Parte del motivo per cui sto ponendo la domanda è chiarirla. Penso che il concetto sia ampiamente citato in C / C ++
smartcaveman

24

Non si tratta dei thread di Zombie, ma il libro Effective C # ha una sezione sull'implementazione di IDisposable, (articolo 17), che parla di oggetti di Zombie che pensavo potessi trovare interessanti.

Consiglio di leggere il libro stesso, ma l'essenza di questo è che se hai una classe che implementa IDisposable o che contiene un descrittore, l'unica cosa che dovresti fare in entrambi è il rilascio di risorse. Se fai altre cose qui, allora c'è la possibilità che l'oggetto non venga raccolto, ma non sarà accessibile in alcun modo.

Fornisce un esempio simile al seguente:

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

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

Quando viene chiamato il distruttore su questo oggetto, un riferimento a se stesso viene inserito nell'elenco globale, il che significa che rimane vivo e in memoria per la durata del programma, ma non è accessibile. Ciò può significare che le risorse (in particolare le risorse non gestite) potrebbero non essere completamente rilasciate, il che può causare ogni sorta di potenziali problemi.

Un esempio più completo è di seguito. Quando viene raggiunto il ciclo foreach, ci sono 150 oggetti nell'elenco Non morti che contengono ciascuno un'immagine, ma l'immagine è stata GC'd e si ottiene un'eccezione se si tenta di utilizzarla. In questo esempio, ricevo ArgumentException (il parametro non è valido) quando provo a fare qualsiasi cosa con l'immagine, sia che provi a salvarla, sia a visualizzare dimensioni come altezza e larghezza:

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

Ancora una volta, sono consapevole che stavi chiedendo in particolare dei thread di zombi, ma il titolo della domanda riguarda gli zombi in .net, e mi è stato ricordato questo e ho pensato che altri potrebbero trovarlo interessante!


1
Questo è interessante. È _undeadpensato per essere statico?
smartcaveman,

1
Così l'ho provato, con qualche stampa dall'oggetto "distrutto". Lo tratta come un oggetto normale. C'è qualcosa di problematico nel fare questo?
smartcaveman,

1
Ho aggiornato la mia risposta con un esempio che, si spera, dimostra il problema un po 'più chiaramente.
JMK

1
Non so perché sei stato sottratto. L'ho trovato utile. Ecco una domanda: che tipo di eccezione hai intenzione di ottenere? Non mi aspetto che sarebbe un NullReferenceException, perché ho la sensazione che la cosa mancante debba essere più legata alla macchina che all'applicazione. È giusto?
smartcaveman,

4
Sicuramente non è tanto IDisposableil problema. Ho la preoccupazione con i finalizzatori (distruttori) ma il fatto di IDisposablenon far passare un oggetto nella coda dei finalizzatori e rischiare questo scenario di zombi. Questo avviso riguarda i finalizzatori e potrebbero chiamare metodi Dispose. Ci sono esempi in cui IDisposableviene utilizzato nei tipi senza finalizzatori. Il sentimento dovrebbe essere per la pulizia delle risorse, ma potrebbe essere una pulizia non banale delle risorse. RX utilizza validamente IDisposableper la pulizia degli abbonamenti e può chiamare altre risorse a valle. (Non ho votato neanche a proposito ...)
James World,

21

Sui sistemi critici sottoposti a carichi pesanti, la scrittura di codice senza blocco è migliore principalmente a causa dei miglioramenti delle prestazioni. Guarda cose come LMAX e come sfrutta la "simpatia meccanica" per grandi discussioni su questo. Preoccupati per i fili di zombi però? Penso che sia un caso limite che è solo un bug da appianare, e non un buon motivo per non usarlo lock.

Sembra più che il tuo amico stia solo immaginando e ostentando la sua conoscenza dell'oscura terminologia esotica per me! Durante tutto il periodo in cui gestivo i laboratori di performance di Microsoft UK, non ho mai incontrato un'istanza di questo problema in .NET.


2
Penso che diverse serie di esperienze ci rendano paranoici su diversi bug. Ha riconosciuto che è un caso limite estremo mentre lo spiega, ma se è davvero un caso limite e non un caso mai, mi piacerebbe almeno capirlo un po 'meglio - Grazie per il tuo contributo
smartcaveman

1
Giusto. Non vorrei solo che tu fossi sproporzionatamente preoccupato per l' lockaffermazione!
James World,

1
Sarei molto meno preoccupato se avessi una comprensione più profonda di questo problema.
smartcaveman,

4
Ho votato perché stai cercando di sbarazzarti di un thread-FUD, di cui ce n'è troppo. Lock-FUD impiegherà più lavoro per smaltire :) Mi limito solo quando gli sviluppatori prendono uno spinlock illimitato, (per le prestazioni), in modo che possano copiare 50K di dati su una coda larga.
Martin James,

3
Sì, sia in generale che in particolare. Scrivere il codice corretto senza lock è difficile quanto la programmazione. Nella mia esperienza per la stragrande maggioranza dei casi, i lockblocchi di grasso grosso a grana grossa (relativamente) facili da comprendere vanno bene. Eviterei di introdurre complessità per l'ottimizzazione delle prestazioni fino a quando non saprai di doverlo fare.
James World,

3

1. Esiste una definizione più chiara di "filo zombi" rispetto a quella che ho spiegato qui?

Sono d'accordo che esistono "Discussioni di zombi", è un termine che si riferisce a ciò che accade con le discussioni che sono lasciate con risorse che non lasciano andare e che tuttavia non muoiono completamente, da cui il nome "zombi", quindi il tuo la spiegazione di questo rinvio è piuttosto giusta per i soldi!

2. Possono esserci thread di zombi su .NET? (Perché perché no?)

Sì, possono verificarsi. È un riferimento e in realtà indicato da Windows come "zombie": MSDN utilizza la parola "Zombie" per processi / thread morti

Accadere spesso è un'altra storia, e dipende dalle tue tecniche e pratiche di codifica, come per te che ti piace il Thread Locking e lo hai fatto per un po ', non mi preoccuperei nemmeno di quello scenario che ti sta accadendo.

E sì, come @KevinPanko ha giustamente menzionato nei commenti, "Zombie Threads" proviene da Unix, motivo per cui vengono utilizzati in XCode-ObjectiveC e indicati come "NSZombie" e utilizzati per il debug. Si comporta più o meno allo stesso modo ... l'unica differenza è che un oggetto che sarebbe dovuto morire diventa uno "ZombieObject" per il debug invece del "Zombie Thread" che potrebbe essere un potenziale problema nel tuo codice.


1
Ma il modo in cui MSDN utilizza lo zombi è molto diverso dal modo in cui questa domanda lo utilizza.
Ben Voigt,

1
Oh sì, sono d'accordo, ma stavo sottolineando che anche MSDN si riferisce ai thread come Zombie Threads quando è morto. e che in effetti possono verificarsi.
SH

4
Certo, ma si riferisce a qualche altro codice che mantiene ancora un handle al thread, non il thread che tiene gli handle alle risorse quando è uscito. La tua prima frase, concordando con la definizione nella domanda, è ciò che è sbagliato.
Ben Voigt,

1
Ahh capisco il tuo punto. Ad essere sincero, non me ne sono nemmeno accorto. Mi sono concentrato sul suo punto che la definizione esiste, indipendentemente da come accade. Ricorda che la definizione esiste a causa di ciò che accade al thread e non di come viene eseguita.
SH

0

Sono in grado di creare thread di zombi abbastanza facilmente.

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

Ciò perde le maniglie del filo (per Join() ). È solo un'altra perdita di memoria per quanto riguarda il mondo gestito.

Ora, uccidere un thread in un modo che detiene effettivamente le serrature è un dolore nella parte posteriore, ma possibile. L'altro ExitThread()fa il lavoro. Come ha scoperto, l'handle del file è stato ripulito dal gc ma locknon lo farebbe attorno a un oggetto. Ma perché dovresti farlo?

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.