Mutex yerine ne zaman bir spinlock kullanılmalıdır?


294

Her ikisinin de aynı işi yaptığını düşünüyorum, senkronizasyon için hangisini kullanacağınıza nasıl karar veriyorsunuz?



11
Mutex ve Semaphore aynı şey değil, bu yüzden bunun bir kopya olduğunu düşünmüyorum. Başvurulan makalenin Cevabı bunu doğru bir şekilde belirtmektedir. Daha fazla ayrıntı için bkz. Barrgroup.com/Embedded-Systems/How-To/RTOS-Mutex-Semaphore
nanoquack

Yanıtlar:


730

Teori

Teoride, bir iş parçacığı bir muteksi kilitlemeye çalıştığında ve başarılı olmazsa, muteks zaten kilitli olduğundan, hemen başka bir iş parçacığının çalışmasına izin verecek şekilde uykuya geçecektir. Uyandırılıncaya kadar uyumaya devam edecek, bu da muteks kilidin daha önce kilitte tuttuğu her iplik tarafından açıldıktan sonra geçerli olacaktır. Bir iş parçacığı bir döndürme kilidini kilitlemeye çalıştığında ve başarılı olmadığında, sonunda başarılı olana kadar sürekli olarak yeniden kilitlemeyi dener; bu nedenle başka bir iş parçacığının yerini almasına izin vermez (bununla birlikte, mevcut iş parçacığının CPU çalışma zamanı kuantumu aşıldığında, işletim sistemi zorla başka bir iş parçacığına geçer).

Sorun

Mutekslerle ilgili sorun, ipliklerin uykuya alınması ve tekrar uyanmasının hem oldukça pahalı işlemler olması hem de oldukça fazla CPU talimatına ihtiyaç duymaları ve bu nedenle biraz zaman almalarıdır. Eğer muteks sadece çok kısa bir süre için kilitlenmişse, bir ipliğin uykuya alınması ve tekrar uyandırılması için harcanan zaman, ipliğin gerçekte uyuduğu süreyi aşabilir ve hatta ipliğin geçeceği süreyi aşabilir bir spinlock üzerinde sürekli yoklama yaparak israf var. Öte yandan, bir döndürme kilidindeki yoklama sürekli CPU zamanını boşa harcar ve kilit daha uzun bir süre tutulursa, bu çok daha fazla CPU zamanı harcar ve bunun yerine iplik uyuyorsa çok daha iyi olurdu.

Çözüm

Tek çekirdekli / tek CPU sisteminde spinlock kullanmak genellikle bir anlam ifade etmez, çünkü spinlock yoklaması yalnızca mevcut CPU çekirdeğini bloke ettiği sürece başka hiçbir iş parçacığı çalışamaz ve başka hiçbir iş parçacığı çalışamayacağı için kilit çalışmaz kilidini açmak. IOW, bir spinlock, gerçek bir fayda sağlamak için bu sistemlerde yalnızca CPU süresini harcar. Eğer iplik uykuya daldıysa, başka bir iplik bir kerede koşmuş olabilir, muhtemelen kilidin kilidini açabilir ve sonra tekrar uyandıktan sonra ilk ipliğin işlemeye devam etmesine izin verebilirdi.

Çok çekirdekli / çok işlemcili sistemlerde, yalnızca çok kısa bir süre için tutulan çok sayıda kilit bulunan, sürekli iş parçacığı uyku moduna geçirmek ve tekrar uyanmak için harcanan zaman, çalışma zamanı performansını önemli ölçüde düşürebilir. Bunun yerine spinlock'ları kullanırken, iş parçacıkları tam çalışma zamanı kuantumlarından yararlanma şansına sahip olurlar (her zaman sadece çok kısa bir süre için engellerler, ancak daha sonra çalışmalarına hemen devam ederler), bu da çok daha yüksek işlem hacmine yol açar.

Pratik

Programcılar sıklıkla mutekslerin veya spinlock'ların daha iyi olup olmayacağını önceden bilemediğinden (örn. Hedef mimarinin CPU çekirdeği sayısı bilinmediğinden) veya işletim sistemleri belirli bir kod parçasının tek çekirdekli veya çok çekirdekli ortamlarda, çoğu sistem muteksler ve spinlock'lar arasında kesin bir ayrım yapmaz. Aslında, çoğu modern işletim sistemi hibrid mutekslere ve hibrit spinloklara sahiptir. Bu aslında ne anlama geliyor?

Hibrit bir muteks, ilk başta çok çekirdekli bir sistemde spinlock gibi davranır. Bir iş parçacığı muteksi kilitleyemezse, muteks yakında açılabileceğinden hemen uykuya konulmaz, bu nedenle muteks önce tam olarak bir spinlock gibi davranacaktır. Sadece belirli bir süre sonra kilit elde edilmemişse (veya tekrar dener veya başka bir ölçüm faktörü), iplik gerçekten uyku moduna geçirilir. Aynı kod sadece tek çekirdekli bir sistemde çalışıyorsa, muteks dönmez, ancak yukarıda görüldüğü gibi, bu yararlı olmaz.

Bir hibrit spinlock ilk başta normal bir spinlock gibi davranır, ancak çok fazla CPU zamanını boşa harcamamak için bir geri çekilme stratejisi olabilir. Genellikle ipliği uykuya geçirmez (çünkü bir spinlock kullanırken olmasını istemezsiniz), ancak ipliği durdurmaya (hemen veya belirli bir süre sonra) karar verebilir ve başka bir iş parçacığının çalışmasına izin verebilir Böylece, spinlock kilidinin açılması şansı artar (saf bir iplik anahtarı genellikle bir ipliği uykuya sokmayı ve daha sonra tekrar olmasa da daha sonra tekrar uyandırmayı içeren bir düğmeden daha ucuzdur).

özet

Şüphe duyuyorsanız, muteksleri kullanın, genellikle daha iyi bir seçimdir ve çoğu modern sistem, yararlı görünüyorsa, çok kısa bir süre için dönmelerine izin verecektir. Spinlock'ları kullanmak bazen performansı artırabilir, ancak yalnızca belirli koşullar altında ve şüphe duyduğunuzda bana bir spinlock'un faydalı olabileceği herhangi bir proje üzerinde çalışmadığınızı söyler. Dahili olarak bir spinlock veya mutex kullanabilen kendi "kilit nesnesini" kullanmayı düşünebilirsiniz (örneğin, bu davranış böyle bir nesne oluştururken yapılandırılabilir), başlangıçta her yerde muteksler kullanın ve bir yerde spinlock kullanmanın gerçekten olabileceğini düşünüyorsanız yardım edin, deneyin ve sonuçları karşılaştırın (örneğin bir profil oluşturucu), ancak her iki durumu da test ettiğinizden emin olun,

Güncelleme: iOS için Uyarı

Aslında iOS'a özgü değil, ancak iOS çoğu geliştiricinin bu sorunla karşılaşabileceği bir platformdur: Sisteminizde bir iplik zamanlayıcı varsa, önceliği ne kadar düşük olursa olsun, herhangi bir iş parçacığının sonunda çalışma şansı elde edeceğini garanti etmez, spinloklar kalıcı kilitlenmelere yol açabilir. İOS zamanlayıcı, daha düşük bir sınıftaki farklı iş parçacığı sınıflarını ve iş parçacıklarını yalnızca daha yüksek bir sınıftaki hiçbir iş parçacığı da çalışmak istemiyorsa çalışır. Bunun için bir back-off stratejisi yoktur, bu nedenle kalıcı olarak yüksek sınıf iş parçacıkları varsa, düşük sınıf iş parçacıkları hiçbir zaman CPU zamanı elde etmez ve bu nedenle hiçbir zaman herhangi bir iş yapma şansı yoktur.

Sorun aşağıdaki gibi görünür: Kodunuz, düşük prio sınıfı bir iş parçacığında bir spinlock alır ve bu kilidin ortasında, zaman kuantum aşıldı ve iş parçacığı çalışmayı durdurur. Bu spinlock'un tekrar serbest bırakılmasının tek yolu, düşük prio sınıfı iş parçacığının tekrar CPU zamanı alması ancak bunun garanti edilmemesi. Sürekli olarak çalıştırmak isteyen birkaç yüksek prio sınıfı iş parçacığına sahip olabilirsiniz ve görev zamanlayıcısı her zaman bunlara öncelik verir. Bunlardan biri dönüş kilidinin üzerinden geçebilir ve elde etmeye çalışabilir, ki bu elbette mümkün değildir ve sistem verimi sağlayacaktır. Sorun şudur: Geri dönen bir iplik hemen tekrar çalışmaya hazırdır! Kilidi tutan iplikten daha yüksek bir prioya sahip olan kilidi tutan iplik CPU çalışma zamanını elde etme şansı yoktur.

Mutekslerde bu sorun neden oluşmuyor? Yüksek prio iplik muteksi elde edemediğinde verim vermez, biraz dönebilir ancak sonunda uykuya gönderilir. Uyku olayı bir olay tarafından uyandırılıncaya kadar çalıştırılamaz, örneğin, bekleyen muteks kilidi gibi bir olay. Apple bu sorunun farkındadır ve OSSpinLocksonuç olarak kullanımdan kaldırılmıştır . Yeni kilit çağrılır os_unfair_lock. Bu kilit, farklı iplik önceliği sınıflarının farkında olduğu için yukarıda belirtilen durumdan kaçınır. İOS projenizde spinlock kullanmanın iyi bir fikir olduğundan eminseniz, bunu kullanın. Uzak durOSSpinLock! Ve hiçbir koşulda iOS'ta kendi spinlock'larınızı uygulamayın! Şüpheniz varsa, bir muteks kullanın! macOS, CPU zamanında herhangi bir iş parçacığının (düşük prio iş parçacıkları bile) "kuru çalışmasına" izin vermeyen farklı bir iş parçacığı zamanlayıcısına sahip olduğundan bu sorundan etkilenmez, yine de aynı durum orada ortaya çıkabilir ve daha sonra çok zayıf dolayısıyla OSSpinLockmacOS'ta da kullanımdan kaldırılmıştır.


3
mükemmel açıklama ... Ben spinlock i hakkında bir şüphem var, e ISR bir spinlock kullanabilir miyim? hayır ise neden hayır
haris

4
@Mecki Yanılmıyorsam, cevabınızda zaman dilimlemenin sadece tek işlemcili sistemlerde gerçekleştiğini önermişsinizdir. Bu doğru değil! Tek işlemcili bir sistemde bir döndürme kilidi kullanabilirsiniz ve süre kuantumunun süresi dolana kadar dönecektir. Daha sonra aynı önceliğe sahip başka bir iş parçacığı ele geçirebilir (tıpkı çoklu işlemcili sistemler için tanımladığınız gibi).
fumoboy007

7
@ fumoboy007 "ve zaman kuantumunun süresi doluncaya kadar dönecektir" // Bu, CPU zamanını / pil gücünü tamamen tek bir fayda olmadan kesinlikle hiçbir şey için boşa harcamanız anlamına gelir, ki bu tamamen moroniktir. Ve hayır, hiçbir zaman zaman dilimlemenin sadece tek çekirdekli sistemlerde gerçekleştiğini söylemedim, tek çekirdekli sistemlerde SADECE zaman dilimleme olduğunu söyledim , GERÇEK paralellik om çok çekirdekli sistemler (ve aynı zamanda zaman dilimleme, ancak benim yazdıklarım için alakasız) ) cevap; Ayrıca, hibrit bir spinlock'un ne olduğu ve neden tek ve çok çekirdekli sistemlerde iyi çalıştığı konusunu tamamen kaçırdınız.
Mecki

11
@ fumoboy007 A İş parçacığı kilidi tutar ve kesilir. B iş parçacığı çalışır ve kilidi ister, ancak alamaz, bu yüzden döner. Çok çekirdekli bir sistemde, B İş parçacığı hala dönerken A İş parçacığı başka bir çekirdek üzerinde çalışmaya devam edebilir, kilidi serbest bırakır ve B İş Parçacığı geçerli zaman kuantumunda devam edebilir. Tek çekirdekli bir sistemde, kilidi açmak için sadece bir A çekirdeği A vidası çalıştırılabilir ve bu çekirdek B İpliği eğirme ile meşgul edilir. Dolayısıyla, İplik B zaman kuantumunu aşmadan spinlock'un serbest bırakılmasının hiçbir yolu yoktur ve bu nedenle tüm eğirme sadece zaman kaybıdır.
Mecki

2
Linux çekirdeğinde uygulanan spinlocks ve mutexes hakkında daha fazla bilgi edinmek istiyorsanız , bölüm 5 harika bir Linux Aygıt Sürücüleri, Üçüncü Baskı (LDD3) (muteksler: sayfa 109; spinlocks: sayfa 116).
patryk.beza

7

Mecki'nin önerisiyle devam eden bu makale , Alexander Sandler'in blogunda mutex vs pthread spinlock adlı Linux makalesinde Alex, #ifdef kullanarak davranışı test etmek için nasıl spinlock& nasıl uygulanabileceğini gösteriyor mutexes.

Bununla birlikte, gözleminize göre son çağrıyı aldığınızdan emin olun, verilen örnek izole bir durum, proje gereksiniminiz, ortamınız tamamen farklı olabilir.


6

Ayrıca, belirli ortamlarda ve koşullarda (dağıtım düzeyindeki pencerelerde çalıştırma = = DISPATCH LEVEL gibi) mutex'i değil, spinlock'u kullanabileceğinizi unutmayın. Unix'te - aynı şey.

İşte rakip stackexchange unix sitesinde eşdeğer bir soru: /unix/5107/why-are-spin-locks-good-choices-in-linux-kernel-design-instead-of-something- Daha

Windows sistemlerinde gönderim hakkında bilgi: http://download.microsoft.com/download/e/b/a/eba1050f-a31d-436b-9281-92cdfeae4b45/IRQL_thread.doc


6

Mecki'nin cevabı oldukça iyi çiviler. Ancak, tek bir işlemcide, görev bir Kesinti Hizmeti yordamı tarafından verilmek üzere kilidi beklerken döndürme kilidi kullanmak anlamlı olabilir. Kesinti, kontrolü ISR'ye aktarır ve bu da kaynağı bekleyen görev tarafından kullanıma hazır hale getirir. Kesilen göreve kontrolü vermeden önce kilidi serbest bırakarak sona erer. Eğirme görevi, eğirme kilidini bulabilir ve devam eder.


2
Bu cevaba tam olarak katılıyorum. Tek bir işlemci, eğer bir görev bir kaynak üzerinde kilit tutarsa, ISR güvenli bir şekilde devam edemez ve görevin kaynağın kilidini açmasını bekleyemez (kaynağı tutan görev kesintiye uğradığı için). Bu gibi durumlarda, görev kendisi ve ISR arasında dışlamayı zorlamak için kesintileri devre dışı bırakmalıdır. Tabii ki, bu çok kısa zaman aralıkları için yapılmalıdır.
user1202136

3

Spinlock ve Mutex senkronizasyon mekanizmaları günümüzde oldukça yaygındır.

Önce Spinlock'u düşünelim.

Temelde meşgul bir bekleme eylemidir, yani bir sonraki eyleme geçmeden önce belirli bir kilidin serbest bırakılmasını beklememiz gerekir. Kavramsal olarak çok basit, uygularken durum böyle değil. Örneğin: Kilit serbest bırakılmadıysa, iplik takas edildi ve uyku durumuna geçti, bununla ilgilenmeli miyiz? İki iş parçacığı aynı anda erişim istediğinde senkronizasyon kilitleriyle nasıl başa çıkılır?

Genel olarak, en sezgisel fikir, kritik bölümü korumak için bir değişken üzerinden senkronizasyon ile uğraşmaktır. Mutex kavramı benzer, ancak yine de farklı. Odaklan: CPU kullanımı. Spinlock, eylemi beklemek için CPU zamanını tüketir ve bu nedenle, ikisi arasındaki farkı özetleyebiliriz:

Homojen çok çekirdekli ortamlarda, kritik bölümdeki zaman harcaması Spinlock kullanmaktan daha azsa, bağlam değiştirme süresini azaltabiliriz. (Tek çekirdekli karşılaştırma önemli değildir, çünkü bazı sistemlerin anahtarın ortasında Spinlock uygulaması)

Windows'ta, Spinlock kullanımı iş parçacığını DISPATCH_LEVEL'e yükseltir, bu da bazı durumlarda izin verilmeyebilir, bu nedenle bu sefer Mutex (APC_LEVEL) kullanmamız gerekti.


-6

Tek çekirdekli / tek CPU sisteminde spinlocks kullanmak genellikle bir anlam ifade etmez, çünkü spinlock yoklaması yalnızca mevcut CPU çekirdeğini bloke ettiği sürece başka hiçbir iş parçacığı çalışamaz ve başka hiçbir iş parçacığı çalışamayacağı için kilit çalışmaz kilidini açmak. IOW, bir spinlock, gerçek bir fayda sağlamak için bu sistemlerde yalnızca CPU zamanını harcar

Bu yanlış. Uni işlemci sistemlerinde spinlock'ları kullanmada cpu döngüleri israfı yoktur, çünkü bir işlem bir spin kilidi aldığında, önleme devre dışı bırakılır, bu nedenle, başka kimse dönemez! Sadece onu kullanmanın bir anlamı yok! Bu nedenle, Uni sistemlerindeki spinloklar derleme zamanında çekirdek tarafından preempt_disable ile değiştirilir!


Alıntılananlar hala tamamen doğrudur. Kaynak kodun derlenmiş sonucu döndürme kilitleri içermiyorsa, alıntı yapılan önemsizdir. Çekirdeklerin derleme zamanında değiştirilmesiyle ilgili söylediklerinizin doğru olduğunu varsayarsak, yalnızca çekirdeğin kendisinde bulunan spinloklar hakkında kesinlikle konuşmadıkça, spinloklar tek işlemcili olabilecek veya olmayabilir.
Hydranix

"İşlem bir dönüş kilidi aldığında, önleme devre dışı bırakılır". Bir işlem sıkıldığında önleme devre dışı bırakılmaz. Bu durumda, tek bir işlem sadece bir spinlock girip asla ayrılmadan tüm makineyi yıkabilir. İş parçacığınızın çekirdek alanı (kullanıcı alanı yerine) çalışıyorsa, bir spin kilidi almak gerçekten preemption devre dışı bırakır, ancak burada tartışılan şey olduğunu sanmıyorum.
Konstantin Weitz

At derleme zamanında tarafından kernel ?
Shien

@konstantin FYI döndürme kilidi yalnızca çekirdek boşluğunda alınabilir. Sıkma kilidi alındığında yerel işlemcide önleme devre dışı bırakılır.
Neelansh Mittal

Açıkçası CONFIG_SMP etkin olan bir çekirdeğe karşı bir modül derleyemez ve aynı modülü CONFIG_SMP devre dışı olan bir çekirdek üzerinde çalıştıramazsınız.
Neelansh Mittal
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.