1. Golang'da defer özelliğine giriş

Go dilinde, defer ifadesi, içinde bulunduğu fonksiyonun çalışmasını tamamlayıp bitene kadar erteler. Diğer programlama dillerindeki finally bloğuna benzetebilirsiniz, ancak defer'ın kullanımı daha esnek ve benzersizdir.

defer kullanmanın faydası, dosyaları kapatma, mutex'lari kilitleme veya sadece bir fonksiyonun çıkış zamanını kaydetme gibi temizleme görevlerini yapmak için kullanılabilmesidir. Bu, programı daha sağlam hale getirebilir ve istisna işleme konusundaki programlama işini azaltabilir. Go'nun tasarım felsefesinde, defer kullanımı, hataları ele alırken, kaynak temizliği yaparken ve diğer sonraki işlemleri yaparken kodu kısa tutmaya ve okunabilir tutmaya yardımcı olduğu için önerilmektedir.

2. defer'ın çalışma prensibi

2.1. Temel çalışma prensibi

defer'ın temel çalışma prensibi, her ertelenmiş fonksiyonu çalıştırmak için bir yığın (son giren, ilk çıkar prensibi) kullanmaktır. Bir defer ifadesi ortaya çıktığında, Go dili derhal ifadeyi takip eden fonksiyonu çalıştırmaz. Bunun yerine, onu ayırt edilmiş bir yığına iter. Şimdiye kadar sona ermesi planlanan dış fonksiyon, bu ertelenmiş fonksiyonları yığın sırasına göre sırayla çalıştırır, en son tanımlanan defer ifadesindeki fonksiyon en önce çalıştırılır.

Ayrıca, defer ifadesini takip eden fonksiyonlardaki parametrelerin, gerçek yürütülmede hesaplanıp sabitlendiğini, ertelendiği anda dikkate almak önemlidir.

func ornek() {
    defer fmt.Println("dünya") // ertelendi
    fmt.Println("merhaba")
}

func main() {
    ornek()
}

Yukarıdaki kod şunu çıktılar:

merhaba
dünya

dünya, örnek fonksiyonu çıkışından önce yazdırılır, kod içinde merhaba'dan önce görünmesine rağmen.

2.2. Birden fazla defer ifadesinin çalıştırılma sırası

Bir fonksiyonda birden fazla defer ifadesi olduğunda, bunlar son giren, ilk çıkar prensibine göre çalıştırılır. Bu, karmaşık temizleme mantığını anlamak için genellikle çok önemlidir. Aşağıdaki örnek, birden fazla defer ifadesinin çalıştırılma sırasını göstermektedir:

func cokluDeferred() {
    defer fmt.Println("İlk erteleme")
    defer fmt.Println("İkinci erteleme")
    defer fmt.Println("Üçüncü erteleme")

    fmt.Println("Fonksiyon bloğu")
}

func main() {
    cokluDeferred()
}

Bu kodun çıktısı şu olacaktır:

Fonksiyon bloğu
Üçüncü erteleme
İkinci erteleme
İlk erteleme

Çünkü defer, son giren, ilk çıkar prensibini takip ettiği için, "İlk erteleme" en son ertelenen olsa da en son olarak çalıştırılır.

3. Farklı senaryolardaki defer'ın uygulamaları

3.1. Kaynak serbest bırakma

Go dilinde, defer ifadesi, dosya işlemleri ve veritabanı bağlantıları gibi kaynak serbest bırakma mantığını ele almak için yaygın olarak kullanılır. defer, fonksiyonun herhangi bir nedenle çıkmasına bakılmaksızın, ilgili kaynakların doğru bir şekilde serbest bırakılmasını sağlar.

Dosya işlemi örneği:

func DosyaOku(dosyaAdi string) {
    dosya, err := os.Open(dosyaAdi)
    if err != nil {
        log.Fatal(err)
    }
    // Dosyanın kapatılmasını sağlamak için defer kullanın
    defer dosya.Close()

    // Dosya okuma işlemleri yapılır...
}

Bu örnekte, os.Open dosyayı başarıyla açtığında, ardışık defer dosya.Close() ifadesi, fonksiyon sona erdiğinde dosya kaynağının doğru bir şekilde kapatılmasını ve dosya işaretçi kaynağının serbest bırakılmasını sağlar.

Veritabanı Bağlantısı Örneği:

func VeritabaniSorgusu(sorgu string) {
    db, err := sql.Open("mysql", "kullanici:şifre@/veritabanıAdı")
    if err != nil {
        log.Fatal(err)
    }
    // Defer kullanarak veritabanı bağlantısının kapatılmasını sağlayın
    defer db.Close()

    // Veritabanı sorgu işlemleri yapılır...
}

Benzer şekilde, defer db.Close() veritabanı bağlantısının, VeritabaniSorgusu fonksiyonunu terk ettiğinde kapatılmasını, performansın normal dönüş ya da istisna fırlatılmasına bakılmaksızın sağlar.

3.2. Eş zamanlı programlamada Kilit İşlemleri

Eş zamanlı programlamada, mutex kilitlerin serbest bırakılmasını ele almak için defer kullanma iyi bir uygulamadır. Bu, kritik bölüm kodunun yürütülmesinden sonra kilidin doğru bir şekilde serbest bırakılmasını sağlayarak kilitlenmeleri önler.

Mutex Kilit Örneği:

var mutex sync.Mutex

func updateSharedResource() {
    mutex.Lock()
    // Kilidin serbest bırakılmasını sağlamak için defer kullanın
    defer mutex.Unlock()

    // Paylaşılan kaynağa yapılan değişiklikler yapılır...
}

Paylaşılan kaynağın değiştirilip değiştirilmediğine bakılmaksızın veya araya bir panik girse bile, defer diğer gorutinlerin kilidi almasına izin vermek için Unlock()'un çağrılmasını sağlayacaktır.

İpucu: Mutex kilitlerinin detaylı açıklamaları sonraki bölümlerde ele alınacaktır. Bu noktada defer'ın uygulama senaryolarını anlamak yeterlidir.

defer için 3 Ortak Sorun ve Dikkat Edilmesi Gerekenler

defer kullanırken, kodun okunabilirliği ve bakımı büyük ölçüde artsa da, dikkate alınması gereken bazı sorunlar ve konular da mevcuttur.

3.1 Defer'a aktarılan fonksiyon parametreleri hemen değerlendirilir

func printValue(v int) {
    fmt.Println("Değer:", v)
}

func main() {
    value := 1
    defer printValue(value)
    // `value`'un değerini değiştirmek, defer'a zaten iletilmiş olan parametreyi etkilemeyecektir
    value = 2
}
// Çıktı "Değer: 1" olacaktır

Defer ifadesinden sonra value'nun değeri değişse bile, defer'da printValue'a iletilen parametre zaten değerlendirilmiş ve sabitlendiği için çıktı hala "Değer: 1" olacaktır.

3.2 Döngüler içinde defer kullanırken dikkatli olun

Döngü içinde defer kullanmak, kaynakların döngü bitmeden serbest bırakılmamasına ve bu durumun kaynak sızıntılarına veya tükenmeye yol açmasına neden olabilir.

3.3 Eş zamanlı programlamada "kullanımdan sonra serbest bırak"tan kaçının

Eş zamanlı programlarda, kaynakları serbest bırakmak için defer kullanırken, kaynağa erişmeye çalışacak olan tüm gorutinlerin kaynağa erişmeye çalışmamasını sağlamak için serbest bırakıldıktan sonra, yarış koşullarını önlemek önemlidir.

4. defer ifadelerinin yürütme sırasına dikkat edin

defer ifadeleri Son Giren İlk Çıkar (LIFO) prensibini takip eder, yani en son tanımlanan defer önce yürütülür.

Çözümler ve En İyi Uygulamalar:

  • Defer ifadelerindeki fonksiyon parametrelerinin ifadenin tanımlandığı zamanda değerlendirildiğini her zaman farkında olun.
  • Döngü içinde defer kullanırken, anonim fonksiyonlar kullanmayı veya kaynak serbest bırakımını açıkça çağırmayı düşünün.
  • Eş zamanlı bir ortamda, kaynakları serbest bırakmak için defer kullanmadan önce, tüm gorutinlerin işlemlerini tamamladığından emin olun.
  • Birden fazla defer ifadesi içeren fonksiyonlar yazarken, yürütme sıralarını ve mantığı dikkatlice düşünün.

Bu en iyi uygulamaları takip etmek, defer kullanımı sırasında karşılaşılan çoğu sorunu önlemenizi sağlar ve daha sağlam ve bakımı kolay Go kodu yazmanıza yol açar.