ProgramlamaGo geliştirici

Go'da dosya ve kaynakların işlenmesinde defer'in çalışma özelliği nedir? Sızıntılardan nasıl kaçınabilir ve doğru serbest bırakmayı nasıl garanti edebilirsiniz?

Hintsage yapay zeka asistanı ile mülakatları geçin

Cevap.

Sorunun tarihi

Go, kaynak yönetimi konusunda pragmatik bir yaklaşım benimsemiştir. Diğer dillerden tanıdık olan try-finally yerine, burada defer bulunmaktadır: çıkış alanında "ertelenmiş" bir işlev kaydeden yerleşik bir mekanizma ve işlevi görünüm alanından çıkarken yerine getirmektedir. Bu araç, kaynakların otomatik serbest bırakılması (dosyalar, ağ bağlantıları) için sıkça kullanılmaktadır.

Sorun

Eğer bir dosya veya bağlantı üzerinde Close çağrısını yapmayı unutursanız, kaynak sızıntısı veya kilitlenme meydana gelebilir — bu, sunucu ve dosya uygulamaları için kritik öneme sahiptir. Defer ile ertelenmiş çağrı, hata veya panic durumunda bile tamamlanma işlevinin çağrılmasını garanti eder. Ancak defer'in yanlış kullanımı belirli durumlarda hatalara yol açabilir: bir döngüde defer çağrısı yapmak, yanlış nesne geçirmek veya büyük sayıda defer ile çalışmak overhead'e neden olabilir.

Çözüm

Her zaman defer f.Close() çağrısını, kaynağı başarıyla açtıktan hemen sonra yapın, böylece kapatmayı unutmaktan kaçınabilirsiniz. Çok sayıda dosya açılıyorsa hız ve bellek tasarrufu amacıyla sıkı döngülerde defer kullanmayın. Dosya/kaynak açmayı en aza indirmek için bir işlevde sarmaya çalışın.

Kod örneği:

file, err := os.Open("data.txt") if err != nil { log.Fatal(err) } defer file.Close() // garantili kapama // ... dosyayla çalışma

Anahtar özellikler:

  • defer her zaman panic durumunda bile yürütülür, ancak named return'den sonra
  • defer çağrısının sırası katı bir LIFO'dur
  • tight loop içinde büyük tahsisatlarla etkisizdir

Kandırıcı sorular.

Defer ne zaman yürütülür ve parametreleri ne zaman hesaplanır?

Defer'deki işlev parametreleri, defer'in beyanı anında hemen hesaplanır, yürütme anında değil.

Kod örneği:

func main() { a := 1 defer fmt.Println(a) // 1'i hatırlar a = 42 } // 1'i yazdırır

Defer bellek sızıntısına veya yavaşlamalara neden olabilir mi?

Evet, eğer bir döngü içinde defer kullanıyorsanız ve çok sayıda dosya veya nesne açıyorsanız, her defer, işlevden çıkışta temizlenen ertelemeli çağrı yığınının içerisine kaydedilir, bu da gereksiz bellek artışına yol açar.

Kod örneği:

for i := 0; i < 10000; i++ { f, _ := os.Open("file.txt") defer f.Close() // tüm 10000 dosyayı main sonunda açık tutar }

Eğer f.Close() çağrısı bir hata dönerse ve bu göz ardı edilirse ne olur?

Kaynak kapama hatasını günlüğe kaydetmek standart bir uygulamadır. Bu nokta göz ardı edilirse, arızaları veya geçici dosyanın silinmesi gibi kısmi dosya kaydedilmesi durumlarını fark etmeyebilirsiniz.

Tipik hatalar ve anti-paternler

  • defer bir döngü içinde çağrılır => kaynak sızıntısı
  • Close()'dan gelen hata işlenmez
  • kaynağın yaşam alanına uygun defer

Hayattan bir örnek

Olumsuz durum

Dosya işleme döngüsünde geliştirici, her dosya için defer f.Close() koyar. Bunun sonucunda aynı anda on binlerce dosya açık kalıyor, programın çalışması yavaşlıyor ve sistemde dosya tanımlayıcıları bitiyor.

Artıları:

  • Çok basit bir ifade

Eksileri:

  • Kontrolsüz kaynak artışı, potansiyel panic sistemi (çok fazla açık dosya)

Olumlu durum

Döngü içinde her dosya, defer f.Close() yalnızca bir kez çağrılarak ve hemen kaynağı serbest bırakarak ayrı bir işlev içinde işleniyor.

Artıları:

  • Kaynaklar her zaman zamanında serbest bırakılır
  • Performans kaybı yok

Eksileri:

  • Kodun işlevsel bölünmesi, iyi bir yapı gerektirir.