2017-05-09 1 views
0

Tenir compte de l'extrait de code suivant:Collision entre garbage collector et fonctions différées?

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

Ce morceau de code est légitime, et fonctionnera OK. Les dossiers seront fermés au retour de a() Cependant, ce qui suit ne fonctionnera pas correctement:

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

L'erreur qui sera reçu, à l'occasion, sera bad file descriptor, en raison du fait de NewFile setting a finalizer qui, lors de la collecte des ordures , va fermer le fichier lui-même. Ce qui n'est pas clair pour moi, c'est que la fonction différée a toujours une référence au fichier, donc théoriquement, il ne devrait pas encore être collecté. Alors, pourquoi golang runtime se comporte-t-il de cette façon?

+0

connexes/possible duplicatr de [Dans Go, quand une variable devient inaccessible?] (Http://stackoverflow.com/questions/37588639/in-go-when-will-a-variable- devenir inatteignable/37591282 # 37591282) – icza

Répondre

4

les problèmes du code est après file.Fd() retour, file est inaccessible, donc file peut être proche par le finaliseur (ordures recueillies).

selon runtime.SetFinalizer:

Par exemple, si p points à une structure qui contient un descripteur de fichier d, et p a une finaliseur qui ferme ce descripteur de fichier, et si la dernière utilisation de p dans un function est un appel à syscall.Write (pd, buf, size), alors p peut être inaccessible dès que le programme entre dans syscall.Write. Le finaliseur peut s'exécuter à ce moment, fermant p.d, provoquant l'échec de syscall.Write car il écrit dans un descripteur de fichier fermé (ou, pire, dans un descripteur de fichier complètement différent ouvert par un autre goroutine). Pour éviter ce problème, appelez runtime.KeepAlive (p) après l'appel de syscall.Write.

runtime.KeepAlive utilisation:

KeepAlive marque son argument comme actuellement accessible. Cela garantit que l'objet n'est pas libéré et que son finalizer n'est pas exécuté avant le point dans le programme où KeepAlive est appelé.

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