Rust, C ++ 'nın eşzamanlılık tesislerinden nasıl ayrılır?


35

Sorular

Rust'un C + 'nin eşzamanlılık olanaklarını temelde ve yeterince geliştirip geliştirmediğini anlamak için çalışıyorum.

Spesifik olarak, idiomatic Rust, idiomatic C ++ 'nın eşzamanlılık olanaklarından nasıl ya da farklı bir oranda farklılaşır?

Gelişme (veya ayrışma) çoğunlukla sözdizimsel midir, yoksa paradigmada bir gelişme (ayrışma) mıdır? Yoksa başka bir şey mi var? Yoksa bu gerçekten bir gelişme (ayrışma) değil midir?


gerekçe

Geçenlerde kendime C ++ 14'ün eşzamanlılık olanaklarını öğretmeye çalıştım ve bir şeyler pek doğru gelmiyor. Bir şey hissediyor. Neler hissediyor? Söylemesi zor.

Sanki derleyici eşzamanlılık söz konusu olduğunda doğru programları yazmam için bana yardım etmeye çalışmıyor gibiydi. Sanki derleyici yerine bir assembler kullanıyormuşum gibi geliyor.

Kuşkusuz, eşzamanlılık söz konusu olduğunda, henüz ince ve hatalı bir konseptten muzdarip olmam muhtemel. Belki de Bartosz Milewski'nin durumsal programlama ve veri yarışları arasındaki gerginliği henüz kırmadım. Belki de derleyicide ne kadar ses eşzamanlı metodolojisinin ve OS'de ne kadarının olduğunu tam olarak anlamadım.

Yanıtlar:


56

Daha iyi bir eşzamanlılık hikayesi, Rust projesinin ana hedeflerinden biridir, bu nedenle projeye hedeflerine ulaşmak için güvendiğimiz sürece iyileştirmeler beklenmelidir. Tam feragatname: Rust hakkında yüksek bir fikrim var ve yatırım yapıyorum. İstendiği gibi, değer yargılarından kaçınmaya ve (IMHO) iyileştirmeler yerine farklılıkları tanımlamaya çalışacağım .

Güvenli ve güvensiz Pas

"Rust" iki dilden oluşur: Sizi, sistem programlamanın tehlikelerinden ayırmaya çok çalışan ve böyle bir özlem olmadan daha güçlü bir dil.

Güvensiz Rust, C ++ 'a çok benzeyen pis, kaba bir dildir. Keyfi olarak tehlikeli şeyler yapmanıza, donanımla konuşmanıza, (yanlış) belleği manuel olarak yönetmenize, kendinizi ayağınıza vurmanıza vb. İzin verir. Programın doğruluğunun elinizde olması, C ve C ++ gibi ve buna dahil olan diğer tüm programcıların elleri. Anahtar kelimeyle bu dili tercih edersiniz unsafeve C ve C ++ 'da olduğu gibi, tek bir konumdaki tek bir hata tüm projenin çökmesine neden olabilir.

Güvenli Rust "varsayılan" dır, Rust kodunun büyük çoğunluğu güvenlidir ve anahtar kelimeyi unsafekodunuzda asla belirtmezseniz , güvenli dili asla terketmezsiniz. Görevin geri kalan kısmı çoğunlukla bu dille ilgilenecektir, çünkü unsafekod, Rust'ın size sağlaması için çok çalıştığı garantilerinin tümünü ve tümünü bozabilir. Kapak tarafında, unsafekod kötü değildir ve toplum tarafından böyle muamele edilmez (ancak, gerekli olmadığında kesinlikle önerilmez).

Tehlikelidir, evet, ama aynı zamanda önemlidir, çünkü güvenli kodun kullandığı soyutlamaları oluşturmaya izin verir. İyi güvensiz kod, başkalarının kötüye kullanmasını önlemek için type sistemini kullanır ve bu nedenle bir Rust programında güvensiz kodun bulunması güvenlik kodunu bozmaz. Aşağıdaki tür farklar, Rust'un tip sistemlerinde C ++ 'nın sahip olmadığı araçlara sahip olması ve eşzamanlılık soyutlamalarını uygulayan güvensiz kodun bu araçları etkin bir şekilde kullanması nedeniyle ortaya çıkar.

Farksızlık: Paylaşılan / değiştirilebilir hafıza

Rust, mesaj iletmeye daha fazla önem vermesine ve paylaşılan hafızayı çok sıkı bir şekilde kontrol etmesine rağmen, paylaşılan hafızanın eşzamanlılığını dışlar ve ortak soyutlamaları (kilitler, atomik işlemler, koşul değişkenleri, eşzamanlı koleksiyonlar) açıkça destekler.

Dahası, C ++ gibi ve fonksiyonel dillerin aksine, Rust gerçekten geleneksel zorunlu veri yapılarını sever. Standart kütüphanede kalıcı / değişmez bağlantılı bir liste yoktur. Var std::collections::LinkedListama bu std::listC ++ ' da olduğu gibi ve aynı nedenle std::list(önbellek kullanımı kötü) aynı cesarete sahip değil .

Bununla birlikte, bu bölümün başlığına ("paylaşılan / değiştirilebilen bellek") atıfta bulunarak, Rust, C ++ 'a göre bir farklılığa sahiptir: Belleğin "paylaşılan XOR değişebilir" olmasını, yani aynı bellekte asla paylaşılmamasını ve değiştirilememelerini şiddetle teşvik eder. saati. Demek istediğin gibi "kendi iş parçanın mahremiyetinde" olarak belleği değiştir. Bunu, paylaşılabilir değişken hafızanın varsayılan seçenek olduğu ve yaygın olarak kullanıldığı C ++ ile karşılaştırın.

Paylaşılan-xor-değişebilir paradigması aşağıdaki farklar için çok önemli olsa da, alışması biraz zaman alan ve önemli kısıtlamalar getiren oldukça farklı bir programlama paradigmasıdır. Bazen, bu paradigmadan, örneğin atomik tiplerden vazgeçmek zorunda kalır AtomicUsize(paylaşılan değiştirilebilir hafızanın özüdür). Kilitlerin aynı zamanda, paylaşılan-xor-değişken kuralına da uyduğunu unutmayın, çünkü eşzamanlı okuma ve yazma işlemlerini hariç tutar (bir konu yazar, başka konu okunamaz veya yazı yazamaz).

Farksızlık: Veri yarışları tanımsız davranışlardır (UB)

Rust kodunda bir veri yarışını tetiklerseniz, oyun biter, tıpkı C ++ 'da olduğu gibi. Tüm bahisler kapalıdır ve derleyici ne isterse yapabilir.

Bununla birlikte, güvenli Rust kodunun veri yarışlarının (veya bu konuda herhangi bir UB'nin) bulunmaması kesin bir garantidir . Bu, hem ana dile hem de standart kütüphaneye uzanır. unsafeUB'yi tetikleyen, kullanmayan (üçüncü taraf kütüphanelerinde de dahil olmak üzere standart kütüphaneyi hariç) içeren bir Rust programı yazabilirsiniz , o zaman bu bir hata olarak kabul edilir ve giderilir (bu zaten birkaç kez olmuştur). Tabii ki bu, tabiki C ++ ile karşılaştırıldığında, UB ile program yazmanın önemsiz olduğu durumlar.

Fark: Sıkı kilitleme disiplini

C aksine ++ Rust (bir kilit std::sync::Mutex, std::sync::RwLockvs.) sahip olan bu koruduğunu verileri. Kilit almak ve ardından yalnızca belgelerde kilitle ilişkilendirilmiş bazı paylaşılan belleği değiştirmek yerine, kilidi tutmuyorsanız paylaşılan verilere erişilemez. Bir RAII koruması kilidi tutar ve aynı anda kilitli verilere erişim sağlar (bu kadarı C ++ tarafından uygulanabilir, ancak std::kilitler tarafından değil ). Ömür boyu sistemi, kilidi açtıktan sonra verilere erişememenizi sağlar (RAII korumasını bırakın).

Elbette, yararlı veri içermeyen bir kilitleme ( Mutex<()>) olabilir ve bu belleği kilitlemeyle açıkça ilişkilendirmeden bir miktar belleği paylaşabilirsiniz. Ancak, potansiyel olarak senkronize edilmemiş paylaşılan belleğe sahip olmak gerekir unsafe.

Fark: Kazara paylaşımın önlenmesi

Paylaşılan hafızayı paylaşabilseniz de, bunu açıkça istediğinizde paylaşırsınız. Örneğin, mesaj iletmeyi kullandığınızda (örneğin kanalları std::sync), ömür boyu sistemi, başka bir iş parçacığına gönderdikten sonra verilere herhangi bir referans tutmamanızı sağlar. Bir kilidin arkasındaki verileri paylaşmak için, kilidi açıkça oluşturup başka bir iş parçacığına verirsiniz. Senkronize edilmemiş hafızayı unsafesizinle paylaşmak için kullanmanız gerekir unsafe.

Bu bir sonraki noktaya bağlanır:

Fark: İş güvenliği izleme

Rust'un tip sistemi bir miktar iplik güvenliği nosyonunu izler. Spesifik olarak, bu Syncözellik, veri yarışları riski olmadan birkaç iş parçacığı tarafından paylaşılabilen tipleri belirtirken Send, bir iş parçacığından diğerine taşınabilecekleri işaretler. Bu program boyunca derleyici tarafından uygulanır ve bu nedenle kütüphane tasarımcıları bu statik kontroller olmadan aptalca tehlikeli olabilecek optimizasyonlar yapmaya cesaret ederler. Örneğin, birkaç iş parçacığı tarafından kullanılması std::shared_ptrdurumunda UB'yi önlemek için referans sayısını değiştirmek için her zaman atomik işlemleri kullanan C ++ 'lar shared_ptr. Pas vardır Rcve Arcyalnızca bu farklılık, hangi Rc kullanımlar olmayan atom refcount operasyonları ve ÅŸan (yani uygulamıyor değil Syncya Send) iken Arcçok çok gibishared_ptr (ve her iki özelliği de uygular).

Bir tür eğer Not gelmez kullanmak unsafeSenkranizasyonu uygulamak, özelliklerin varlığı veya yokluğu doğru çıkarılmaktadır.

Fark: Çok katı kurallar

Derleyici, bazı kodların veri yarışlarından ve diğer UB’lerden arınmış olduğundan kesinlikle emin olamazsa, dönem oluşturmaz . Yukarıda belirtilen kurallar ve diğer araçlar sizi oldukça uzağa götürebilir, ancak er ya da geç doğru olan bir şey yapmak isteyeceksiniz, ancak derleyicinin bildiriminden kaçan ince nedenlerle. Zor bir kilit içermeyen veri yapısı olabilir, ancak "Paylaşılan bir dizide rastgele konumlara yazarım, ancak endeksler her konumun tek bir iş parçacığı tarafından yazılacağı şekilde" hesaplanır.

Bu noktada, mermiyi ısırıp biraz gereksiz senkronizasyon ekleyebilirsiniz veya kodu, derleyicinin doğruluğunu görebilecek şekilde sık sık (bazen yapılabilir, bazen oldukça zor, bazen imkansız) veya unsafekodun içine bırakabilirsiniz . Yine de, ekstra bir zihinsel ek yüküdür ve Rust size unsafekodun doğruluğu konusunda hiçbir garanti vermez .

Fark: Daha az araç

Yukarıda bahsedilen farklılıklar nedeniyle, Rust'ta bir kişinin bir veri yarışına sahip olabilecek kod yazması (ya da ücretsiz sonra kullanması ya da iki kez serbest olması ya da ...) olması çok nadirdir. Bu hoş olsa da, bu tür hataları izlemeye yönelik ekosistemin toplumun gençliği ve küçüklüğü göz önüne alındığında beklenenden daha az gelişmiş olması talihsiz bir yan etkiye sahip.

Valgrind ve LLVM'nin diş temizleyicileri gibi aletler prensip olarak Rust koduna uygulanabilirken, bunun gerçekten işe yarayıp yaramadığı, takımdan takıya değişebilir (ve işe yarayanlar bile, özellikle de hiçbir şey bulamadığınız için) -tarihi kaynaklar nasıl yapılır?). Rust'un şu anda gerçek bir spesifikasyondan ve özellikle de resmi bir bellek modelinden yoksun olması gerçekten yardımcı olmuyor.

Kısacası, unsafeRust kodunu doğru yazmak, her iki dilin de kabiliyetler ve riskler açısından kabaca karşılaştırılabilir olmasına rağmen, C ++ kodunu doğru yazmaktan daha zordur . Elbette bu, tipik bir Rust programının yalnızca nispeten küçük bir unsafekod kesri içereceği gerçeğine karşı ağırlıklandırılmalıdır , oysa bir C ++ programı, tam olarak C ++.


6
+25 yükseltme düğmesi ekranımın neresinde? Bulamıyorum! Bu bilgilendirici cevap çok takdir edilmektedir. Bana kapsadığı noktalarda bariz sorular sormuyor. Öyleyse, diğer noktalara: Eğer Rust'un dokümantasyonunu anlarsam, Rust [a] entegre test olanaklarına ve [b] Cargo adlı bir inşa sistemine sahiptir. Bunlar sizce makul ölçüde üretime hazır mı? Ayrıca, Cargo ile ilgili olarak, yapım sürecine kabuk, Python ve Perl betikleri, LaTeX derlemesi vb. Eklememe izin vermem iyi olur mu?
thb

2
@thb Test malzemeleri çok çıplak kemikler (ör. alaysız) ancak işlevseldir. Kargo, Rust ve tekrar üretilebilirlik üzerine odaklanmasına rağmen, oldukça iyi çalışıyor, ancak kaynak kodundan son eserlere kadar tüm adımları kapsayan en iyi seçenek olmayabilir. Derleme komut dosyaları yazabilirsiniz ancak bu bahsettiğiniz her şey için uygun olmayabilir. (Bununla birlikte, insanlar düzenli olarak C kitaplıklarını derlemek ya da C kitaplıklarının mevcut sürümlerini bulmak için derleme komut dosyaları kullanırlar; bu nedenle, daha fazla Pas kullandığınızda kargo durdurulamaz.)

2
Bu arada, buna değer, cevabınız oldukça kesin görünüyor. C ++ 'ı sevdiğim için, C ++' ın yapmak zorunda olduğum hemen hemen her şey için uygun tesisleri bulunduğundan, C ++ kararlı ve yaygın bir şekilde kullanıldığından, şimdiye kadar her olası hafif olmayan amaç için C ++ 'ı kullanmaktan oldukça memnun oldum (Java’ya hiç ilgi duymadım) , Örneğin). Ama şimdi eşzamanlılık var ve C ++ 14 görünüyor bana onunla mücadele edilecek. On yıl içinde gönüllü olarak yeni bir programlama dili denemedim, ancak (Haskell daha iyi bir seçenek görünmüyorsa) Rust'u denemek zorunda kalacağımı düşünüyorum.
thb

Note that if a type doesn't use unsafe to manually implement synchronization, the presence or absence of the traits are inferred correctly.Aslında hala unsafeelemanları ile bile yapıyor . Sadece ham işaretçiler değildir Syncve Sharebu da onları içeren varsayılan yapıların hiçbirine sahip olmayacağı anlamına gelir.
Hauleth,

@ AsukaszNiemier Tamam olabilir, ancak güvenli olmayan bir tipin kurması için bir yol var Sendya Syncda gerçekten de yapmamasına rağmen.

-2

Pas da Erlang ve Go gibi. Tampon ve koşullu beklemeye sahip kanalları kullanarak iletişim kurar. Tıpkı Go gibi, paylaşılan hafızayı bırakmanıza, atomik referans sayma ve kilitlemelere destek vermenize ve kanalları iplikten dişe geçirmenize izin vererek Erlang'ın kısıtlamalarını gevşetir.

Ancak, Rust bir adım daha ileri gider. Go, doğru olanı yapmanıza güveniyor olsa da, Rust sizinle birlikte oturan bir danışman atar ve yanlış bir şey yapmaya kalkarsanız şikayet eder. Pas'ın akıl hocası derleyicidir. İpliklerin etrafından geçen değerlerin sahipliğini belirlemek ve potansiyel problemler varsa derleme hataları sağlamak için karmaşık analizler yapar.

Aşağıdaki RUST docs'dan bir alıntı.

Sahiplik kuralları mesaj göndermede hayati bir rol oynar, çünkü güvenli, eşzamanlı kod yazmamıza yardımcı olurlar. Eşzamanlı programlamada hataları önlemek, Rust programlarımızda sahiplik hakkında düşünmek zorunda kalmanın getirdiği avantajdır. - Değerlerin mülkiyeti ile geçen mesaj.

Erlang ejderhası ise ve Go özgür bir devletse, Rust dadı bir devlettir.

Programlama Dillerinin Eşzamanlılık ideolojilerinden daha fazla bilgi bulabilirsiniz : Java, C #, C, C +, Go ve Rust


2
Stack Exchange'e Hoş Geldiniz! Lütfen kendi blogunuza ne zaman bağlanırsanız, açıkça belirtmeniz gerektiğini unutmayın; bkz yardım merkezine .
Glorfindel
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.