ProgramlamaKıdemli Go Geliştirici

Go'da global değişkenlerle çalışmanın özellikleri nelerdir, bunların yanlış başlatılmasını ve çalışma zamanı rekabetini (init sırası) nasıl önleyebilirsiniz? Tipik tuzaklar nelerdir?

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

Cevap.

Go'da global değişkenler dosyaların bildirim sırasına ve ilgili init fonksiyonlarının sırasına göre başlatılır. Bu, global duruma doğru bir şekilde erişimi etkiler: bir paketteki bir değişken başka bir paketin değişkeninin değerine bağlıysa, ancak bu paket henüz başlatılmamışsa — bir hata veya belirsiz davranış oluşur.

Farklı paketlerin init fonksiyonları arasında "başlangıç sırası" doğrudan tanımlanamaz. Derleyici, bu sırayı ithalat grafiğine göre belirler.

  • Tüm global değişkenler, main.main() fonksiyonunun çağrılmasından önce başlatılır.
  • Paketteki init sırasında aşırı bağımlılıklar ile durum başlatılması önerilmez!
  • Global değişkenlerin thread güvenliğini sağlamak için sync.Once veya mutex'ler, karmaşık başlatma durumlarında kullanılır.

Güvenli, gecikmeli başlatma örneği (lazy init):

var cfg *Config var once sync.Once func GetConfig() *Config { once.Do(func() { cfg = LoadConfigFromDisk() }) return cfg }

Kandırmaca soru.

Soru: Farklı paketlerin init fonksiyonları Go programı çalıştırıldığında aynı anda (paralel) çalışabilir mi?

Doğru cevap: Hayır, init fonksiyonları ve global değişkenler, paketler arasındaki bağımlılıkların analizi sırasında derleyici tarafından belirlenen sırayla sıkı bir şekilde sırayla çalışır. Init fonksiyonlarının paralel çalışması gerçekleşmez.


Konuyla ilgili ince detayları bilmemekten kaynaklanan gerçek hata örnekleri.


Hikaye

Büyük bir projede birkaç modül, global konfigürasyonları diskten init() aracılığıyla yükleyordu. Bir modül diğerine bağımlıydı, ancak go.mod grafiğinin yeniden düzenlenmesi nedeniyle başlatma sırası değişti — ve aniden konfigürasyon değerleri boş çıktı! Hata, derleme sırasına bağlı olarak rastgele bir şekilde belirdi ve yalnızca bağımlılıkların analizi ve başlatmayı açık bir fonksiyona taşıdıktan sonra bulundu.


Hikaye

REST servisinde rehberlerin önbelleklemesi için mutex olmadan global bir map kullanılıyordu. Başlatma, init'te başlatılan birkaç goroutine'den başlamıştı. Sonuç olarak — data race, dönemsel panik, map içinde geçersiz veriler. sync.Once ile değiştirilip başlatma açıkça çağrıldığında sorun ortadan kalktı.


Hikaye

Global logger, yardımcı paketlerden birinin init fonksiyonunda yaratılıyordu. Ancak başka bir paket, bu logger'a başlatma sırasında, henüz başlatılmadan erişiyordu. Sonuç olarak, başlatma ile ilgili logların bir kısmı kayboluyordu, çünkü logger henüz mevcut değildi. Çözüm — bağımlılık enjekte edilmesi ve logger'ın tüm servisler çalışmadan önce açık bir şekilde başlatılmasıdır.