Go ist eine Sprache mit automatischer Speicherverwaltung: Der Garbage Collector übernimmt ohne Programmierer die Rückgewinnung von Speicher, der von Objekten verwendet wird, die nicht mehr vom Programm verwendet werden. Die gesamte Automatisierung ist jedoch durch den Arbeitsspeicher begrenzt. Wir müssen uns weiterhin selbst um den Rest der vom Programm verwendeten Ressourcen kümmern.
, - , runtime.SetFinalizer. , , . Go - , , , , .
res1, err := NewResource1()
if err != nil {
return nil, err
}
res2, err := NewResource2(res1)
if err != nil {
res1.Close()
return nil, err
}
res3, err := NewResource3(res2)
if err != nil {
res2.Close()
res1.Close()
return nil, err
}
v, err := res3.DoSomething()
if err != nil {
res3.Close()
res2.Close()
res1.Close()
return nil, err
}
res3.Close()
res2.Close()
res1.Close()
return v, nil
. , C# Java using statement try-with-resources statement . Go , , defer statement. , :
res1, err := NewResource1()
if err != nil {
return nil, err
}
defer res1.Close()
res2, err := NewResource2(res1)
if err != nil {
return nil, err
}
defer res2.Close()
res3, err := NewResource3(res2)
if err != nil {
return nil, err
}
defer res3.Close()
return res3.DoSomething()
Close . Close - .
, . - defer . , , - , . , , . , , - , - - , (, main), , , , , .
, Wire. ( Wire) . . cleanup function, . .
Dedicated finalization
, c cleanup function, Wire, . , Close ( ) :
;
.
, , , . , Go :
res, cleanup, err := NewResource()
if err != nil {
return err
}
// cleanup, .
if err := res.DoSomething(); err != nil {
return err
}
, (, ) ( ) . , " ", , , "" "" , .
Composite finalization
defer (, ), :
func Finalize(finalizers ...func()) {
// .
for i := len(finalizers) - 1; i >= 0; i-- {
func() {
defer func() {
// , .
// multierror :
// 1) ;
// 2) .
recover()
}()
finalizers[i]()
}()
}
}
func NewResource3() (*Resource3, func(), error) {
var (
finalizers []func() //
successful bool //
)
defer func() {
// ,
// -
// .
if !successfull {
Finalize(finalizers...)
}
}()
res1, fin1, err := NewResource1()
if err != nil {
return nil, nil, err
}
finalizers = append(finalizers, fin1)
res2, fin2, err := NewResource2(res1)
if err != nil {
return nil, nil, err
}
finalizers = append(finalizers, fin2)
res3 := &Resource3{
resource2: res2,
}
fin3 := func() {
Finalize(finalizers...)
}
// .
//
// .
successful = true
return res3, fin3, nil
}
Finalize - .
new - error - defer , , , , .
KDone
Ich habe die KDone- Bibliothek mit den oben genannten Tools veröffentlicht. Sie ist Teil des Kata- Projekts , das als nächstes besprochen wird . Wir können davon ausgehen, dass die API im Moment stabil ist und wenn sie sich ändert, ist sie unbedeutend - dennoch ist die Bibliothek noch frisch und ich verwende bei unvorhergesehenen Änderungen immer noch die Null-Hauptversion.
Ein typischer Konstruktor, der diese Bibliothek verwendet, sieht folgendermaßen aus:
func NewResource(...) (res Resource, dtor kdone.Destructor, err error) {
defer kerror.Catch(&err) //
// KError.
// .
reaper := kdone.NewReaper() // reaper.
defer reaper.MustFinalize() //
// .
// ... reaper.MustAssume(dtor) ... // reaper
//
// .
return res, reaper.MustRelease(), nil // reaper
//
// .
}
Was denken Sie? Das Konzept ist einfach genug, aber vielleicht habe ich etwas in meinen Überlegungen übersehen? Oder haben Sie Verbesserungsvorschläge? Ich würde mich über Diskussionen in den Kommentaren freuen.