2017-05-09 4 views
0

Betrachten Sie den folgenden Code-Schnipsel:Kollision zwischen Garbage Collector und verzögerten Funktionen?

func a(fd int) { 
file := os.NewFile(uintptr(fd), "") 
defer func() { 
    if err := file.Close(); err != nil { 
     fmt.Printf("%v", err) 
    } 
} 

Dieses Stück Code ist echt, und OK arbeiten. Dateien werden von a() jedoch bei der Rückkehr geschlossen werden, werden die folgenden wird nicht korrekt funktionieren:

func a(fd int) { 
file := os.NewFile(uintptr(fd), "") 
defer func() { 
    if err := syscall.Close(int(file.Fd()); err != nil { 
     fmt.Printf("%v", err) 
    } 
} 

Der Fehler, der gelegentlich empfangen werden, werden bad file descriptor sein, aufgrund der Tatsache der NewFile setting a finalizer , die während der Garbage Collection , wird die Datei selbst schließen.

Was mir unklar ist, ist, dass die Deferred-Funktion immer noch einen Verweis auf die Datei hat, so dass theoretisch noch kein Müll gesammelt werden sollte. Also warum verhält sich Golal Runtime so?

+0

Verwandte/möglich duplicatr von [In Go, wird, wenn eine Variable wird nicht erreichbar?] (Http://stackoverflow.com/questions/37588639/in-go-when-will-a-variable- werden-unerreichbar/37591282 # 37591282) – icza

Antwort

4

die Probleme des Codes ist nach file.Fd() zurück, file ist nicht erreichbar, so file kann in der Nähe von der Finalizer (Müll gesammelt) sein.

nach runtime.SetFinalizer:

Wenn beispielsweise P zeigt auf eine Struktur, die einen Dateideskriptor d enthält, und p hat einen Destruktor, der das Dateideskriptor schließt, und wenn die letzte Verwendung von p in a Funktion ist ein Aufruf von syscall.Write (pd, buf, size), dann kann p nicht erreichbar sein, sobald das Programm syscall.Write eingibt. Der Finalizer kann zu diesem Zeitpunkt ausgeführt werden, indem p.d geschlossen wird, was dazu führt, dass syscall.Write fehlschlägt, weil er in einen geschlossenen Dateideskriptor schreibt (oder schlimmer noch in einen völlig anderen Dateideskriptor, der von einer anderen Goroutine geöffnet wird). Rufen Sie runtime.KeepAlive (p) nach dem Aufruf von syscall.Write auf, um dieses Problem zu vermeiden.

runtime.KeepAlive Nutzung:

Keep-Alive markiert das Argument als gerade erreichbar sind. Dadurch wird sichergestellt, dass das Objekt nicht freigegeben wird und sein Finalizer nicht vor dem Punkt im Programm ausgeführt wird, an dem KeepAlive aufgerufen wird.

func a(fd int) { 
    file := os.NewFile(uintptr(fd), "") 
    defer func() { 
     if err := syscall.Close(int(file.Fd()); err != nil { 
      fmt.Printf("%v", err) 
     } 
     runtime.KeepAlive(file) 
    }() 
}