E se qualche lingua trattasse le risorse non esposte come un errore? [chiuso]

2

Ho visto un sacco di codice come il seguente esempio. È in Python, ma lo stesso errore è stato fatto in tutte le lingue con risorse gestite:

f = open('foo.txt', 'rb')
for line in f: print line

Questo è tutto. L'errore è che close(f) non è stato chiamato in modo tale che l'handle del file sia mantenuto aperto fino a un certo tempo indeterministico in futuro quando la gestione della memoria runtime decide di recuperare la memoria.

Python ha with e c # ha using per semplificare la pulizia delle risorse, ma ignoriamo queste funzionalità per un minuto. Dato che:

  1. È un errore di programmazione non chiudere esplicitamente i file aperti.
  2. Il runtime può rilevare che un file aperto non è stato chiuso.

Perché allora il runtime non genera un errore invece di essere "utile" e chiude il file per il programmatore? Quello sarebbe il fallimento veloce e fallire presto la strategia. C'è una ragione tecnica per cui non può? C'è qualche lingua che lo fa? L'idea è stata presa in considerazione prima (ho cercato su google ma non ho trovato nulla)?

Ecco come quasi implementa la funzione in Python:

class mustclose:
    def __init__(self, f):
        self.f = f
    def __del__(self):
        if not self.f.closed:
            raise Exception("You forgot to close() me!")
k = mustclose(open('foo.txt', 'wb'))
#k.f.close()

Due problemi: richiede wrapper, a Python non piace lanciare eccezioni dai distruttori.

(Leggi questo link QUINTA domanda per informazioni sul motivo per cui è spesso auspicabile un errore veloce)

    
posta Björn Lindqvist 02.09.2014 - 19:07
fonte

7 risposte

3

In Java 7 e versioni successive, le risorse che implementano AutoCloseable fa in modo che il compilatore emetta un avviso quando una risorsa non è gestita con try-with-resources .

Combinato con la garbage collection, questo è un modo per garantire che le risorse vengano liberate a un certo punto.

Come ha detto Robert Harvey, il sistema operativo dispone anche dell'intero spazio di memoria di un processo e libera risorse penzolanti. Tuttavia, ciò non garantisce necessariamente che le risorse siano correttamente chiuse (ad esempio, l'invio di un messaggio di "addio" su un socket a un host remoto per liberare risorse remote). Ciò si basa anche sulla chiusura del processo. Un processo di lunga durata potrebbe perdere risorse come connessioni remote, handle di file, ecc.

Le risorse try-with di Java 7 si avvicinano il più vicino possibile a garantire che anche le risorse non locali siano liberate durante l'esecuzione del programma, presupponendo che gli sviluppatori ascoltino gli avvertimenti del compilatore.

    
risposta data 02.09.2014 - 20:27
fonte
2

The runtime can detect that an open file has not been closed.

L'unica volta in cui il runtime può determinare inequivocabilmente che un file aperto deve essere chiuso è al momento dell'arresto del processo, in particolare con framework basati su eventi che possono riutilizzare gli handle di file aperti nei callback, ecc. A meno che l'handle non sia stato definito (e hai detto ignorare C # usando {} e suggerimenti di runtime Try-With-Resources di Java), il runtime NON può determinare se si è verificato un errore.

Poiché il runtime sta eliminando l'intero processo, ai fini della stabilità del sistema, la sua unica risorsa reale è chiudere tutto lo stream aperto e rilasciare le risorse associate.

    
risposta data 02.09.2014 - 19:50
fonte
2

Che cosa succederebbe se un linguaggio generasse errori di runtime, ad es. sugli handle di file trapelati? I coder lo odieranno con una passione fiammeggiante e non andrebbe da nessuna parte.

Il punto di gestione delle risorse è aiutarti a produrre un programma valido con meno sforzo. "Valuable" non significa "perfetto": un programma che perde un handle di file è cattivo, ma spesso non è neanche lontanamente così male come un programma che esplode deliberatamente invece di sprecare risorse, che potrebbero o non potrebbero causare problemi per il tuo caso d'uso.

Se l'unico obiettivo che vale la pena raggiungere era la perfezione, certo, ogni compilatore, run-time e sistema operativo dovrebbero essere il più spietati possibile nel rilevare e terminare i processi che commettono errori, poiché sono per definizione privi di valore. Probabilmente sarebbe un mondo migliore se i clienti non accettassero qualcosa di meno della perfezione e denigrassero spietatamente i pantaloni di qualsiasi venditore che li avesse venduti con una perdita di risorse, ma siamo abbastanza chiari: questo non è così un mondo .

Questo è solo il lato commerciale delle cose; altri hanno sottolineato che non è facile decidere se una maniglia sia effettivamente trapelata o meno. Le lingue con la garbage collection completamente automatica sono in teoria in grado di aggiungere qualsiasi comportamento desiderato al hook 'dispose'; ad esempio, inizialmente Java supponeva che avresti ripulito le risorse nel metodo finalize di una classe. Questo è ormai ampiamente considerato un errore deplorevole, in quanto risulta che le cose accadono in ordine imprevedibile causando più problemi di quanti ne risolva, in particolare se hanno effetti sull'ambiente esterno (e non solo sul pool di memoria strettamente process-privato). In definitiva, non valeva la pena di rendere deterministico il processo di garbage collection solo per consentire questo schema di gestione delle risorse, quando la lingua ha già finally (e ora le risorse con scope) per ottenere la stessa cosa.

    
risposta data 02.09.2014 - 20:16
fonte
2

Sebbene sia stato quasi certamente considerato, è stato scartato perché, nel caso generale, è impossibile da fare.

Che cosa accade se f non è una variabile locale o viene restituito dalla funzione?

A quel punto, hai un equivalente al problema di interruzione. Non c'è modo (nel caso generale) per l'analisi statica di dimostrare in un modo o nell'altro se il file è chiuso da qualche altra parte nel tuo programma (o peggio, in qualcuno che chiama questo codice in una libreria).

    
risposta data 02.09.2014 - 19:41
fonte
1

Quando un processo viene terminato, tutta la memoria allocata viene automaticamente rilasciata al sistema operativo e gli handle di file aperti vengono chiusi. Questa è la tua valvola di sicurezza.

Il problema, naturalmente, è che il sistema operativo (e anche il compilatore, per quello) non sanno che il codice di esempio è un errore. Devi dichiarare, con il tuo intento quando scrivi il tuo codice, quando e dove l'handle del file viene eliminato. Potresti avere un motivo per tenere aperto il file handle.

L'istruzione using e l'interfaccia IDisposable in C # sono progettate specificamente per ripulire risorse non gestite come handle di file.

// FileStream inherits from IDisposable, and implements the Dispose() method.
using (FileStream fs = File.Create(path))
{
    fs.Write(...);
}
// The File Stream is automatically closed here, because the Dispose() method on the 
// IDisposable interface is automatically called when the "using" block goes out of scope.

In un ambiente gestito, la memoria viene generalmente eliminata automaticamente quando si verifica la garbage collection. L'implementazione di using e IDisposable assicura che gli handle di file e altre risorse non gestite vengano correttamente eliminate prima che la memoria venga rilasciata. Puoi anche indicare il tuo specifico intento chiamando Close() esplicitamente sui tuoi file prima che l'oggetto che racchiude vada fuori ambito.

In alcune situazioni, come i repository che restituiscono un IQueryable , può essere un ostacolo mettere un blocco di codice in un'istruzione using , perché la connessione al database può effettivamente chiudersi prima dell'esecuzione della query.

In breve, il sistema operativo e il compilatore non possono leggerti.

    
risposta data 02.09.2014 - 19:16
fonte
0

Il distruttore viene chiamato dopo che l'oggetto esce dall'ambito.

Se f è globale, significa che all'uscita del programma.

Qualcuno potrebbe usare di nuovo quella risorsa f , e il compilatore / runtime non è generalmente in grado di sapere con certezza che ciò non può accadere in nessun caso particolare.

La sua buona pratica è di valutare le variabili il più strettamente possibile ed evitare i globali. Ma questi linguaggi dinamici sono anche usati come linguaggi di scripting e l'ambito può essere prolisso e compromette anche la leggibilità della forma.

In genere è possibile avere migliaia di handle aperti e tutti vengono chiusi automaticamente dal sistema operativo all'uscita del processo (aggraziato o comunque), pertanto i programmatori vengono raramente morsi da risorse inutilmente longeve.

    
risposta data 02.09.2014 - 19:19
fonte
0

Gli errori di runtime non sono uno strumento per disciplinare i programmatori. sono un ultimo rapporto per gestire errori imprevisti che, se non gestiti, lascerebbero uno stato instabile. Quale non è il caso con gli handle di file aperti. Nel grande schema delle cose, non importa quando vengono chiuse.

Il compilatore dovrebbe emettere un avvertimento, se trova risorse non gestite che escono dall'ambito di applicazione senza disporre? Probabilmente. Ma è ancora la decisione dei programmatori se è accettabile lasciare che il GC gestisca la finalizzazione in un tempo arbitrario, o se quegli handle debbano essere chiusi ora .

    
risposta data 02.09.2014 - 20:33
fonte