Arc::make_mut, önce Arc'ın tahsise olan tek güçlü referansı tuttuğunu doğrulayarak iç veriye değiştirilebilir erişim sağlamaya çalışır. Bu kontrolü, güçlü referans sayısı üzerinde Acquire sıralamasıyla atomik bir yükleme kullanarak gerçekleştirir. Eğer sayı tam olarak bir ise, işlem değiştirilebilir bir referans döndürmeye devam eder; aksi takdirde, iç veriyi kopyalar ve Arc'ı yeni tahsise işaret etmek üzere günceller.
use std::sync::Arc; let mut data = Arc::new(5); *Arc::make_mut(&mut data) += 1; // Sadece paylaşılıyorsa kopyalar
Acquire/Release çiftinin önemi, başka bir iş parçacığı Arc'ını düşürdüğünde sayımda Release azaltması yapmasıdır. make_mut'teki Acquire yüklemesi, düşüren iş parçacığı tarafından azaltma öncesinde yapılan tüm bellek yazımlarının mevcut iş parçacığına görünür olmasını sağlar ve iç veride veri yarışlarını önler.
Arc<Config> aracılığıyla yapılandırma güncellemeleri iletirken yüksek verimli bir metrik toplama hizmetini düşünün. Binlerce iş parçacığı, mevcut ayarları okumak için referanslar tutar, ancak yönetici iş parçacığı zaman zaman eşikleri ayarlamak zorundadır; hizmeti yeniden başlatmadan.
Naif yaklaşım, Config'i bir RwLock içine sarmak ve her okuma için kilitlemektir veya paylaşım durumuna bakılmaksızın her küçük güncelleme için yapıyı kopyalamaktır. İlk çözüm, önbellek satırı atlama ve kilit aşamasından etkilenirken, ikincisi, yapılandırma gerçekten benzersiz olduğunda gereksiz tahsisatlar üzerinde bellek ve CPU döngüleri israf eder.
Alternatif, kilitsiz güncellemeler için tehlike işaretçileri ile AtomicPtr kullanmaktır, ancak bu karmaşık manuel bellek yönetimi gerektirir ve hataya yatkındır. Başka bir seçenek, göstergeyi kendisi için atomik değişimlere izin veren bir RwLock<Arc<Config>> kullanmaktır, ancak bu gösterge değişimi için ek bir dolaylama ve kilit ekler.
Ekip, normal durumu optimize ettiği için Arc::make_mut'i seçti: Eğer başka bir iş parçacığı bir referans tutmuyorsa (güçlü sayı 1 ise), yönetici iş parçacığı bellek tahsis etmeden verileri yerinde değiştirir. Eğer yapılandırma paylaşılırsa, otomatik olarak kopyalar. Bu, son okuyucu diğer iş parçacığının Arc'ını bıraktığında (kullanarak Release) yönetici iş parçacığının sonraki kontrolü (kullanarak Acquire) yapılandırma üzerindeki önceki tüm yazımları görmesini sağlamak için katı Acquire/Release anlamları gerektirir ve parçalı okuma önlenir. Sonuç olarak, düşük rekabet altında yapılandırma güncellemeleri için gecikmede %40 azalma sağlandı.
Neden Relaxed sıralaması Arc::make_mut içindeki referans sayım kontrolünde kullanılamaz?
Relaxed işlemleri hiçbir gerçekleşme öncesi garantisi sağlamaz. Eğer make_mut güçlü sayının 1 olup olmadığını kontrol etmek için Relaxed kullanıyorsa, bir diğer iş parçacığının sayım azaltmasını gözlemleyebilir ve o iş parçacığının iç veriye yaptığı yazımları gözlemlemeyebilir. Bu, mevcut iş parçacığının veriyi değiştirirken başka bir iş parçacığının hala mantıksal olarak onu okumasına neden olur, bu da bir veri yarışı yaratır. Acquire, sayının 1'e ulaştığını gördüğümüzde (başka iş parçacığın düşüşündeki Release ile senkronize) veriye yapılan önceki tüm yazımları da gördüğümüzü garanti eder.
Arc::make_mut'nin manuel olarak Arc ile .clone() yaparak kopyalayıp sonrasında değiştirmenin davranışını ne ayırır?**
Manuel kopyalama, aynı tahsise işaret eden yeni bir Arc oluşturur ve güçlü sayıyı en az 2'ye çıkarır. Bu yeni Arc üzerinden iç veriye değiştirilebilir erişim elde edemezsiniz çünkü Arc yalnızca değiştirilemeyen paylaşım sağlar. Arc::make_mut özeldir çünkü sayının 1 olup olmadığını kontrol eder; eğer öyleyse, mevcut tahsise &mut T sağlar. Değilse, veriyi yeni bir tahsise kopyalar ve 1 sayısıyla yapar, orijinal paylaşılan verinin değiştirilemez kalmasını sağlarken, yeni kopyanın benzersiz sahipliğini verir.
Zayıf işaretçiler (Arc::downgrade) Arc::make_mut'nin benzersizlik garantisini nasıl etkiler?
Zayıf işaretçiler güçlü referans sayımına katılmaz. Arc::make_mut, zayıf referansları göz ardı ederek yalnızca güçlü sayıyı kontrol eder. Ancak, zayıf işaretçiler, tahsisat hâlâ mevcutsa güçlü olanlara yükseltilebilir. Eğer make_mut yerinde değiştirime ile devam ederse (güçlü sayı 1 ise) ve başka bir iş parçacığı zayıf bir işaretçiyi yükseltirse, bu yükseltme aynı değiştirilen veriye işaret eden yeni bir Arc oluşturur. Bu güvenlidir çünkü yükseltme, değiştirmenin ardından gerçekleşir ve Rust'ın bellek modeli, yükseltilen işaretçinin tamamen değiştirilmiş değeri görmesini garanti eder. Zayıf sayı, değişikliği önlemez, ancak tüm güçlü referanslar geçici olarak düşürülse bile tahsisi canlı tutar.