ProgramlamaParalel Hesaplama Mühendisi (Rust)

Rust'ta güvenli çoklu iş parçacığı nasıl çalışır: marker-trait'ler Send ve Sync neyi ifade eder, veri aktarımını ve iş parçacıkları arasında veri paylaşımını nasıl kontrol eder ve geliştirici kendi türleri için bu trait'leri nasıl doğru bir şekilde uygulamalı (veya yasaklamalıdır)?

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

Cevap.

Güvenli çoklu iş parçacığı ortamlarında çalışma sorunu programcılar tarafından uzun zamandır karşılaşılmakta ve yarış koşulları, tutarsız veriler ve bellek sızıntıları gibi sorunlarla sürekli olarak karşı karşıya kalınmaktadır. Rust, bu sorunları derleme aşamasında en aza indirmek için Send ve Sync marker-trait’leri ile benzersiz bir yaklaşım gerçekleştirmiştir.

Sorun — iş parçacıkları arasında paylaşılan verilere erişimin kontrol edilmemesi, bu da zor tespit edilebilen hatalara yol açmaktadır. Birçok dilde bu sorumluluk tamamen programcıya aittir, ancak Rust'da derleyici, iş parçacıkları arasında neyin aktarılabileceğini/paylaşılabileceğini kendisi kontrol eder.

Çözüm: Send trait'i, bir nesnenin bir iş parçacığından diğerine güvenli bir şekilde aktarılabilir olduğunu garanti eder. Sync ise bir nesneye farklı iş parçacıklarından referans ile ortak erişim imkanı sağlar. Rust'taki hemen hemen tüm standart türler otomatik olarak bu trait'leri uygular ve özel türler bunları manuel olarak uygulayabilir veya belirli durumlar için impl !Send veya impl !Sync şeklinde yasaklayabilir.

Kod örneği:

use std::sync::{Arc, Mutex}; use std::thread; let counter = Arc::new(Mutex::new(0)); let mut handles = vec![]; for _ in 0..10 { let counter = Arc::clone(&counter); let handle = thread::spawn(move || { let mut num = counter.lock().unwrap(); *num += 1; }); handles.push(handle); } for handle in handles { handle.join().unwrap(); } // counter her zaman 10 olacak, hafıza yarışları olmadan!

Anahtar özellikler:

  • Send, nesnenin iş parçacıkları arasında sahipliğinin aktarımını ifade eder.
  • Sync, nesneye referanslar aracılığıyla güvenli ortak kullanım sağlar.
  • Kendi türleri için trait’lerin uygulanması/yasaklanması, derleme aşamasında davranışı kontrol etmeyi sağlar.

Kandırmaca Sorular.

Ham işaretçileri içeren bir tür Send veya Sync olabilir mi?

Hayır, eğer tür bir raw pointer ya da iş parçacığı güvenliği garantisi olmayan kaynaklar içeriyorsa, bu trait'leri uygulamaz veya geliştiricinin bunları manuel olarak uygulaması gerekir (genellikle unsafe impl Send/Sync ile).

Rc<T> ve RefCell<T> Send veya Sync midir?

Hayır, Rc<T> ve RefCell<T> çoklu iş parçacıkları için güvenli değildir (ne Send, ne de Sync). Çoklu iş parçacığı senaryoları için Arc<T> ve Mutex/RwLock kullanılır.

Eğer bir static değişken Sync’i uygulanmamış bir tür içeriyorsa ne olur?

Rust, böyle bir static değişkenin var olmasına izin vermez: bu değişkenin Sync olması gerekir, aksi takdirde derleyici bir hata verecektir.

Tipik hatalar ve anti-paternler

  • Çoklu iş parçacıklarından ortak erişim sırasında Rc<T> yerine Arc<T> kullanmak.
  • İçerisinde tehlikeli işaretçiler barındıran yapılar geliştirmek ve Send trait'ine otomatik güven.
  • Dikkatli kontrol olmaksızın unsafe impl Send/Sync kullanarak invariant'ları ihlal etmek.

Gerçek hayattan örnek

Olumsuz durumda

Genç bir geliştirici, thread::spawn içinde bir Rc nesnesi koyar - kod, Rc nesnesinin iş parçacıkları arasında aktarılmadığı sürece derlenir. Eğer Rc'yi thread::spawn'dan çıkarmaya çalışırsa, Rc Send’i uygulamadığı için bir derleme hatası alır ve yarış koşullarından korunmaz.

Artılar:

  • Derleyici hemen veri yarışı hatasını önler.

Eksiler:

  • Rc ve Arc arasındaki farkı bilmezseniz, hatayı çözmek zordur.

Olumlu durumda

Çoklu iş parçacığı sayacı için Arc+Mutex kullanılır, tüm iş parçacıkları güvenli bir arabirim aracılığıyla aynı verilerle çalışır. Yarış koşulları yoktur, kod güvenli ve dayanıklıdır.

Artılar:

  • Yarış koşulları yoktur, bellek güvenliği sağlar, davranışı yönetmek için marker-trait kullanıyoruz.

Eksiler:

  • Mutex ve Arc overhead'e sahip olup, iş parçacığı güvenli temel bilgileri bilme gerektirir.