ProgramlamaKıdemli Go Geliştiricisi

Go'daki defer işlevleri nasıl çalışır: nasıl çağrılırlar, hangi sırayla yürütülürler ve kullanımlarıyla ilgili hangi incelikler önemlidir?

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

Yanıt.

defer, Go'da kaynak yönetimini (örneğin dosyalar, mutex, bağlantılar) basitleştirmek için geliştirilmiştir - bir fonksiyonun sonunda işlemlerin gerçekleştirilmesini garanti etmek gereken herhangi bir durumda. Tarihsel olarak diğer dillerde (Java'da finally, try-with-resources) benzer yapılar mevcuttu ancak Go, daha açık ve anlaşılır bir model sunar.

Sorun: Kaynakların her zaman serbest bırakıldığından emin olmak gerekir, hata çıkarsa veya panic olursa bile. Kaynağın iki kez kapatılması veya sızıntı, klasik programlama stilinde yaygın bir sorundur.

Çözüm: Fonksiyon veya metod içinde defer ile tanımlanan her şey, çağrı yığınına yerleştirilir ve fonksiyondan çıkmadan önce ters sırayla yürütülür. Bu, kaynakların, istisnalar (panic) veya erken return durumlarında bile serbest bırakılmasını garanti eder.

Kod örneği:

func processFile() error { f, err := os.Open("filename.txt") if err != nil { return err } defer f.Close() // dosya kapatılması en sona kalacak // dosya ile çalışma return nil }

Temel özellikler:

  • defer işlevleri her zaman LIFO (son giren, ilk çıkar) sırasına göre yürütülür - en son tanımlanan, ilk olarak çağrılır
  • Defer için parametreler anında hesaplanır, fonksiyon ise daha sonra çağrılır
  • Fonksiyon bir panic ile tamamlanırsa bile, tüm defer'ler çağrılır

Kandırmaca Soruları.

Panic oluştuğunda defer'ler çalışacak mı?

Evet! Panic durumunda bile tüm defer işlevleri çağrılacak, bu "finalizasyon"un ana mekanizmasıdır.

Defer'e geçirilen işlevin argümanları ne zaman hesaplanır?

Defer tanımlandığında, gerçekten yürütülürken değil. Bu nedenle, daha sonra değiştirilecek değişkenler kullanılıyorsa, bunu dikkate almak önemlidir:

а := 1 defer fmt.Println(a) a = 2 // 1 yazacak, 2 değil

Defer döngü içinde nasıl çalışır? Bu bellek sızıntısına yol açar mı?

Eğer her döngü iterasyonunda defer kullanılıyorsa, tüm defer'ler yalnızca tüm fonksiyon tamamlandıktan sonra çalıştırılacaktır, her iterasyondan sonra değil - tüm defer işlevlerinin yığını birikir ve bu fazla bellek tüketimine yol açabilir.

for i := 0; i < 3; i++ { defer fmt.Println(i) }

Tipik Hatalar ve Antipatternler

  • Defer'in döngülerde kullanılması, kaynakların (örneğin DB bağlantıları) geç bir şekilde serbest bırakılmasına neden olur
  • Defer'deki değişkenlerin değişmez olduğuna dair tam bir güven - ancak bu değer hemen sabitlenir
  • Ağır kaynakların geç serbest bırakılması, manuel çağrılardan çok daha geç yapılması

Hayattan Bir Örnek

Olumsuz Durum

Bir döngüde binlerce dosya açılıyor ve her biri için defer kullanılıyor. Tüm dosyalar yalnızca fonksiyonun sonunda kapatılacak ve kaynaklar gecikecek, bu da "sızıntıya" - açık dosya limitini aşmaya neden olacak.

Artılar:

  • Kısa notasyon
  • Her durumda serbest bırakma garantisi

Eksiler:

  • Fonksiyon tamamlanana kadar kaynak sızıntısı
  • Defer ile büyük işlemler sırasında hatalar

Olumlu Durum

Döngüde yerel işlevler kullanılıyor, burada defer yalnızca bu dosyanın kapsamı için uygulanıyor, tüm işleyici için değil:

for _, name := range fileNames { func() { f, _ := os.Open(name) defer f.Close() // f ile çalışma }() }

Artılar:

  • Kaynakların anında geri verilmesi
  • Sızıntı yok

Eksiler:

  • Okuması daha zor (ek fonksiyon derinliği)
  • Defer'in kapsamını hatırlamak gerekiyor.