"fatal error: concurrent map read and map write" Burada aslında size
neden crash olduğunu söylüyor, bir map kullanmışsın ancak iki farklı
goroutine aynı anda okuma ve yazma yapmaya çalışıyor.
bunu bir kitabı okurken birisinin gelip okuduğunuz sayfada yazı
yazmaya başkadığını hayal edin, görünmez bir el sayfada cümleleri
değiştiriyor, okumaya başladığınız cümle değişmeye başlarsa başı ile
sonu arasında sizin kafasınızda bir tutarsızlık olacaktır. bu durumda
golang sizi uyarıyor. kafan karışacak böyle şeyler yapma diyor. yine
belki kitabın okuduğunuz yeri değilde okumadığınız yerlerini
değiştirdiğini düşünün, bir kısmını okudunuz, son kısmı değiştirildi,
dolayısı ile kitaba başlarken casus romanı idi, bitişi tarih kitabına
döndü. tutarsızlık diz boyu olur.
nasıl engellersiniz?.
1 - map'i bir struct içerisine yerleştirmek birde sync.Mutex eklemek,
map'e eriştiğiniz her yerde de bu struct'ın func'ların üzerinden
erişmek,
get, set methodları yazıp içerisinde lock, unlock etmek, burada başka
bir sıkıntı çıkıyor özellikle de defer kullanımı ile ilgili, deferred
func'lar hemen çalışmadığı için scheduler üzerine yerleştiriliyor,
func çalışması biter bitmez değilde scheduler ne zaman uygun görürse o
zaman çalıştırıyor, bu map'e erişimde performans düşmesine neden
oluyor zira unlock hemen çalışmadan map üzerinde bir işlem daha
yapmaya çalışılıyor ve tekrar lock etmeden önce unlock'ı
bekliyorsunuz. deferred func kullanmadan yazarsanız daha performanslı
olur.
2 - sync paketinden sync.Map kullanmak, performansı daha yüksek
(yukarıdaki implementasyona göre), sync.map'de ki problem ise
make(map[int]string) gibi bir type tanımlamasının olmaması, keyler ve
value'lar birer interface olarak tanımlı, bu nedenle value'ları
kullanırken type cast' yapmak zorunda kalıyorsunuz.
3. iki farklı map tutmak, diyelim ki, map'iniz oldukça büyük, (100mb
yer kaplayan map var elinizde) ve değişim oranı düşük, (bir yerden
birdata elecek ve o datayı map'e koyacaksınız.
1 yeni bir map oluşturun make ile,
2 eski map'deki verileri bu yeni map'e yerleştirin.
3 güncellemeyi yeni map'de yapın.
eski map'i taşıyan değişkene yeni map'i verin.
atamalar atomic olduğundan sorun yaşamazsınız ancak yeni map'e eski
verileri koyma işlemi bitene kadar memory de çok fazla işlem
yapılacağı için performans düşer. memory kullanımı artar. bu
yukarıdaki durumu belki günde bir iki defa güncellenecek map'ler için
kullanabilirsiniz tabii memory'niz varsa ölçeklenebilirliği çok
düşünmüyorsanız.
mutex ve semafor nedir, ne zaman gerekir gibi konuları okuyun.
bilgileriniz tazelensin.
bonus okuma:
https://dave.cheney.net/2018/05/29/how-the-go-runtime-implements-maps-efficiently-without-generics
Bonus arama: how map's works for different objects with same hash
Saygılar && İyi Çalışmalar
Timu EREN ( a.k.a selam )