Çok iş parçacıklı uygulamalar yazarken karşılaşılan en yaygın sorunlardan biri çıkmazlardır.
Topluluğa sorularım:
Kilitlenme nedir?
Onları nasıl tespit edersiniz?
Onlarla ilgileniyor musun?
Son olarak, onların oluşmasını nasıl önlersiniz?
Çok iş parçacıklı uygulamalar yazarken karşılaşılan en yaygın sorunlardan biri çıkmazlardır.
Topluluğa sorularım:
Kilitlenme nedir?
Onları nasıl tespit edersiniz?
Onlarla ilgileniyor musun?
Son olarak, onların oluşmasını nasıl önlersiniz?
Yanıtlar:
Birden fazla işlem aynı kaynağa aynı anda erişmeye çalıştığında bir kilit oluşur.
Bir işlem kaybedilir ve diğerinin bitmesini beklemek gerekir.
Bir kilitlenme bekleme süreci hala kendinden önceki ilk ihtiyaçlarını bitirmek anlamına başka bir kaynak tutan yok ki oluşur.
Yani, bir örnek:
Kaynak A ve kaynak B, süreç X ve süreç Y tarafından kullanılır
Kilitlenmeleri önlemenin en iyi yolu, süreçlerin bu şekilde geçmesini önlemektir. Bir şeyi olabildiğince kilitleme ihtiyacını azaltın.
Veritabanlarında, tek bir işlemde farklı tablolarda çok fazla değişiklik yapmaktan kaçının, tetikleyicilerden kaçının ve mümkün olduğunca iyimser / kirli / nolock okumalarına geçin.
Suç filmlerinden çıkmaza girme durumu için gerçek bir dünyayı (aslında gerçek olmayan) açıklayayım. Bir suçlunun rehin aldığını ve buna karşı bir polisin de suçlunun dostu olan bir rehinesi olduğunu düşünün. Bu durumda, polis arkadaşının gitmesine izin vermezse suçlu rehineye gitmeyecek. Ayrıca polis, suçlu rehineyi serbest bırakmadıkça, suçlu arkadaşının gitmesine izin vermeyecektir. Bu sonsuz bir güvenilmez durumdur, çünkü her iki taraf da ilk adımın birbirinden ısrar ediyor.
Yani, basitçe, iki iş parçacığının iki farklı kaynağa ihtiyacı olduğunda ve her birinin diğer ihtiyaç duyduğu kaynağın kilidine sahip olması, bir çıkmazdır.
Bir kızla çıkıyorsunuz ve bir tartışmadan bir gün sonra, her iki taraf da birbirinize kalp kırıyor ve özür dilerim ve özlediğim bir çağrı bekliyorum. Bu durumda, her iki taraf da yalnızca bir tanesi diğerinden üzgünüm çağrısı alırsa birbirleriyle iletişim kurmak ister . Her ikisinin de iletişim kurmayacağı ve pasif bir durumda beklemeyeceği için, her ikisi de diğerinin kilitlenme durumu ile sonuçlanan iletişime başlamasını bekleyecektir.
Kilitlenmeler sadece aynı anda alınabilecek iki veya daha fazla kilit olduğunda ve farklı sırayla yakalandığında ortaya çıkar.
Kilitlenmeden kaçınmanın yolları:
Kilitlenmeyi tanımlamak için önce süreci tanımlarım.
Süreç : Bildiğimiz gibi süreç bir program
icradan başka bir şey değildir .
Kaynak : Bir program işlemini yürütmek için bazı kaynaklara ihtiyaç vardır. Kaynak kategorileri arasında bellek, yazıcılar, CPU'lar, açık dosyalar, manyetik bant sürücüleri, CD-ROM'lar vb. Bulunabilir.
Kilitlenme : Kilitlenme, iki veya daha fazla işlemin bazı kaynakları tuttuğu ve daha fazla kaynak edinmeye çalıştığı bir durum veya durumdur ve buradaki işlemleri tamamlayana kadar kaynakları serbest bırakamazlar.
Kilitlenme durumu veya durumu
Yukarıdaki şemada iki P1 ve p2 işlemi vardır ve iki R1 ve R2 kaynağı vardır .
Kaynak R1 , proses P1'e ve kaynak R2 , proses p2'ye tahsis edilir . İşlem P1'in yürütülmesini tamamlamak için R2 kaynağına ihtiyaç vardır , bu nedenle R2 için P1 talebi , ancak R2 zaten P2'ye tahsis edilmiştir .
Aynı şekilde , işleminin tamamlanması için P2 İşleminin R1'e ihtiyacı vardır , ancak R1 zaten P1'e tahsis edilmiştir .
her iki süreç de yürütme işlemlerini tamamlayana kadar ve kaynaklarını serbest bırakamazlar. Yani her ikisi de başka bir kaynak bekliyor ve sonsuza dek bekleyecekler. Yani bu bir DEADLOCK Koşulu.
Kilitlenmenin meydana gelmesi için dört koşul doğru olmalıdır.
ve tüm bu koşullar yukarıdaki şemada karşılanmıştır.
Bir iş parçacığı hiçbir zaman gerçekleşmeyen bir şeyi beklerken bir kilitlenme oluşur.
Tipik olarak, bir iş parçacığı, önceki sahibi tarafından asla serbest bırakılmayan bir muteks veya semaforda beklerken olur.
Ayrıca, iki iş parçacığı ve iki kilit içeren bir durum olduğunda da sık sık olur:
Thread 1 Thread 2
Lock1->Lock(); Lock2->Lock();
WaitForLock2(); WaitForLock1(); <-- Oops!
Genellikle onları algılarsınız çünkü gerçekleşmesini beklediğiniz şeyler asla gerçekleşmez veya uygulama tamamen askıda kalır.
Bu harika makalelere Deadlock bölümünden bakabilirsiniz . C # 'da ama fikir hala diğer platformlar için aynı. Burada kolay okuma için alıntı yapıyorum
İki iş parçacığı diğeri tarafından tutulan bir kaynak beklediğinde bir kilitlenme oluşur, bu nedenle de devam edemez. Bunu göstermenin en kolay yolu iki kilittir:
object locker1 = new object();
object locker2 = new object();
new Thread (() => {
lock (locker1)
{
Thread.Sleep (1000);
lock (locker2); // Deadlock
}
}).Start();
lock (locker2)
{
Thread.Sleep (1000);
lock (locker1); // Deadlock
}
Kilitlenme, işletim sistemindeki çoklu işleme / çoklu programlama sorunlarında yaygın bir sorundur. Diyelim ki iki süreç P1, P2 ve iki global paylaşılabilir kaynak R1, R2 ve kritik bölümde her iki kaynağa da erişilmesi gerekiyor
Başlangıçta, OS P1'i işlemek için R1'i ve P2'yi işlemek için R2'yi atar. Her iki işlem de eşzamanlı olarak çalıştığından kodlarını yürütmeye başlayabilirler ancak SORUN bir işlem kritik bölüme çarptığında ortaya çıkar. Bu yüzden R1 işlemi P2 işleminin R2'yi serbest bırakmasını bekleyecek ve tersi de ... Sonsuza dek bekleyecekler (DEADLOCK CONDITION).
Küçük bir ANALOGİ ...
Anneniz (OS),
Siz (P1),
Kardeşiniz (P2),
Elma (R1),
Bıçak (R2),
kritik bölüm (elmayı bıçakla kesme).Annen başlangıçta sana elma ve bıçağı kardeşine verir.
Her ikisi de mutlu ve oynuyor (Kodlarını yürütmek).
Biriniz elmayı (kritik bölüm) bir noktada kesmek istiyor.
Elmayı kardeşine vermek istemiyorsun.
Kardeşin bıçağı sana vermek istemiyor.
Yani ikiniz de çok uzun bir süre bekleyeceksiniz :)
Kilitlenme, iki dişin her ikisinin de ilerlemesini engelleyen kilitleri ortaya çıkardığında oluşur. Onlardan kaçınmanın en iyi yolu dikkatli gelişimdir. Birçok gömülü sistem, bir bekçi zamanlayıcısı (belirli bir süre askıda kaldığında sistemi sıfırlayan bir zamanlayıcı) kullanarak bunlara karşı koruma sağlar.
Her biri kilitli bir kaynağı tutan ve zincirdeki bir sonraki eleman tarafından tutulan bir kaynağı kilitlemeye çalışan dairesel bir iş parçacığı veya süreç zinciri olduğunda bir kilitlenme oluşur. Örneğin, sırasıyla A ve B kilitlerini tutan ve her ikisi de diğer kilidi almaya çalışan iki iş parçacığı.
Deadlock durumunu anlamak için klasik ve çok basit bir program : -
public class Lazy {
private static boolean initialized = false;
static {
Thread t = new Thread(new Runnable() {
public void run() {
initialized = true;
}
});
t.start();
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
System.out.println(initialized);
}
}
Ana iş parçacığı Lazy.main'i çağırdığında, Lazy sınıfının başlatılıp başlatılmadığını denetler ve sınıfı başlatmaya başlar. Ana iş parçacığı artık false olarak ayarlanmış olarak ayarlanmış, çalışma yöntemi kümeleri true olarak ayarlanmış bir arka plan iş parçacığı oluşturur ve başlatır ve arka plan iş parçacığının tamamlanmasını bekler.
Bu kez, sınıf şu anda başka bir iş parçacığı tarafından başlatılıyor. Bu koşullar altında, arka plan iş parçacığı olan geçerli iş parçacığı, başlatma işlemi tamamlanana kadar Class nesnesinde bekler. Ne yazık ki, başlatmayı yapan iş parçacığı olan ana iş parçacığı, arka plan iş parçacığının tamamlanmasını bekliyor. İki iş parçacığı artık birbirini beklediğinden, program DEADLOCKED.
Kilitlenme, hiçbir işlemin / iş parçacığının bir eylem gerçekleştiremediği bir sistem durumudur. Başkaları tarafından belirtildiği gibi, bir kilitlenme, tipik olarak her işlemin / iş parçacığının, başka (veya aynı) bir işlem / iş parçacığı tarafından zaten kilitlenmiş olan bir kaynağa kilit almak istediği durumun sonucudur.
Onları bulmak ve önlemek için çeşitli yöntemler vardır. Biri çok düşünmek ve / veya çok şey denemek. Bununla birlikte, paralellikle uğraşmak herkesin bildiği gibi zordur ve çoğu (hepsi olmasa da) insanların problemlerden tamamen kaçınamayacaklarıdır.
Bu tür sorunlarla başa çıkmak konusunda ciddiyseniz, bazı daha resmi yöntemler yararlı olabilir. Farkına vardığım en pratik yöntem süreç teorik yaklaşımını kullanmaktır. Burada sisteminizi bazı işlem dillerinde (örn. CCS, CSP, ACP, mCRL2, LOTOS) modelleyebilir ve kilitlenmeleri (ve belki de bazı diğer özellikleri) kontrol etmek için (model-) kullanılabilir araçları kullanabilirsiniz. Kullanılacak araç seti örnekleri FDR, mCRL2, CADP ve Uppaal'dir. Bazı cesur ruhlar, tamamen sembolik yöntemler (teorem kanıtlama; Owicki-Gries'i arayın) kullanarak sistemlerinin kilitlenmesini bile kanıtlayabilir.
Bununla birlikte, bu biçimsel yöntemler tipik olarak biraz çaba gerektirir (örneğin, süreç teorisinin temellerini öğrenmek). Ama sanırım bu sadece bu sorunların zor olmasının bir sonucu.
Kilitlenme, farklı işlem tarafından talep edildiği için kullanılabilir kaynak sayısının az olması durumunda ortaya çıkan bir durumdur. Bu, mevcut kaynakların sayısı kullanıcı tarafından talep edilenden daha az olduğunda, o zaman işlem bekleme durumuna girer.Bazı bekleme süresi daha fazla artar ve kaynak eksikliği sorununu kontrol etme şansı yoktur. bu duruma kilitlenme denir. Aslında, kilitlenme bizim için büyük bir sorundur ve sadece çoklu görev işletim sisteminde meydana gelir. Çift görev tek görevli işletim sisteminde meydana gelemez, çünkü tüm kaynaklar sadece şu anda çalışan görev için mevcuttur ......
Yukarıda bazı açıklamalar güzel. Umarım bu da yararlı olabilir: https://ora-data.blogspot.in/2017/04/deadlock-in-oracle.html
Bir veritabanında, bir oturum (örneğin ora) başka bir oturum tarafından tutulan bir kaynak istediğinde (örn. Veri), ancak bu oturum (veri) aynı zamanda ilk oturum (ora) tarafından tutulan bir kaynak ister. 2'den fazla oturum da olabilir ancak fikir aynı olacaktır. Aslında, Deadlocks bazı işlemlerin çalışmaya devam etmesini önler. Örneğin: ORA-DATA'nın A kilidi tuttuğunu ve B kilidi istediğini ve SKU'nun B kilidi ve A kilidi istediğini varsayalım.
Teşekkürler,
Bir iş parçacığı diğer iş parçacığının bitmesini beklerken ya da tam tersi olduğunda kilitlenme oluşur.
Nasıl önlenir?
- İç İçe Kilitlerden
Kaçının - Gereksiz Kilitlerden Kaçının
- İplik birleştirme kullanın ()
Nasıl tespit edersiniz?
cmd'de bu komutu çalıştırın:
jcmd $PID Thread.print
başvuru : geeksforgeeks
En sık karşılaşılan neden olsa da kilitlenmeler sadece kilitlerle oluşmaz. C ++ 'da, std :: thread nesnesindeki diğeri için her thread çağrısına join () uygulayarak iki iş parçacığı ve kilitsiz kilit oluşturabilirsiniz.
Paylaşılan kaynaklara erişimi kontrol etmek için kilitleme özelliğini kullanmak kilitlenmelere yatkındır ve yalnızca işlem zamanlayıcısı bunların oluşumunu engelleyemez.
Örneğin, ilişkisel veritabanı sistemleri işlem ACID özelliklerini garanti etmek için çeşitli kilitler kullanır .
Hangi ilişkisel veritabanı sistemini kullanırsanız kullanın, belirli bir tablo kaydını değiştirirken (örn. UPDATE
Veya DELETE
) kilitler her zaman edinilir . Halen çalışan bir işlemle değiştirilen bir satırı kilitlemeden Atomicity tehlikeye girer .
Bu makalede açıkladığım gibi , iki eşzamanlı işlem ilerleme kaydedemediğinde bir kilitlenme meydana gelir, çünkü her biri diğerinin aşağıdaki diyagramda gösterildiği gibi bir kilit açmasını bekler.
Her iki işlem de kilit alma aşamasında olduğundan, ikisi de bir sonraki işlemden önce bir kilit açmaz.
Kilitlere dayanan bir Eşzamanlılık Kontrol algoritması kullanıyorsanız, her zaman bir kilitlenme durumunda çalışma riski vardır. Kilitlenmeler sadece bir veritabanı sisteminde değil, herhangi bir eşzamanlılık ortamında meydana gelebilir.
Örneğin, iki veya daha fazla iş parçacığı önceden edinilmiş kilitlerde bekleyen bir iş parçacığı herhangi bir ilerleme sağlayamayacak şekilde kilitlenebilir. Bu bir Java uygulamasında gerçekleşirse, JVM yalnızca bir iş parçacığını yürütülmesini durdurmaya ve kilitlerini serbest bırakmaya zorlayamaz.
Thread
Sınıf bir stop
yöntemi ortaya çıkarsa bile , bu yöntem Java 1.1'den beri kullanımdan kaldırılmıştır, çünkü bir iş parçacığı durdurulduktan sonra nesnelerin tutarsız bir durumda kalmasına neden olabilir. Bunun yerine, Java birinterrupt
kesintiye uğrayan bir iş parçacığı olarak bir ipucu görevi gören yöntemi ve kesintiyi görmezden gelebilir ve yürütülmesine devam edebilir.
Bu nedenle, bir Java uygulaması bir kilitlenme durumundan kurtulamaz ve kilit alma isteklerini kilitlenme asla oluşmayacak şekilde sipariş etmek uygulama geliştiricisinin sorumluluğundadır.
Ancak, belirli bir işlemin başka hangi kilitleri daha fazla elde etmek isteyeceğini öngörmek mümkün olmadığından, bir veritabanı sistemi belirli bir kilit alma siparişini uygulayamaz. Kilit sırasının korunması veri erişim katmanının sorumluluğu haline gelir ve veritabanı yalnızca bir kilitlenme durumundan kurtulmaya yardımcı olabilir.
Veritabanı motoru, geçerli çakışma grafiğini kilit bekleme döngüleri (kilitlenmelerden kaynaklanan) için tarayan ayrı bir işlem yürütür. Bir döngü algılandığında, veritabanı motoru bir işlemi alır ve iptal eder, böylece kilitlerin serbest kalmasına neden olur, böylece diğer işlem ilerleme kaydedebilir.
JVM'den farklı olarak, bir veritabanı işlemi atomik bir iş birimi olarak tasarlanmıştır. Bu nedenle, bir geri alma, veritabanını tutarlı bir durumda bırakır.
Bu konu hakkında daha fazla bilgi için bu makaleye de göz atın.
Mutex özünde, paylaşılan kaynaklara korumalı erişim sağlayan bir kilittir. Linux altında, mutex iş parçacığı veri türü pthread_mutex_t şeklindedir. Kullanmadan önce başlatın.
Paylaşılan kaynaklara erişmek için muteksi kilitlemelisiniz. Muteks zaten kilitteyse, muteks kilidi açılana kadar çağrı diziyi engeller. Paylaşılan kaynaklara yapılan ziyaretin tamamlanmasının ardından, bunların kilidini açmanız gerekir.
Genel olarak, birkaç yazılı olmayan temel ilke vardır:
Paylaşılan kaynakları kullanmadan önce kilidi edinin.
Kilidi olabildiğince kısa sürede tutmak.
İplik bir hata döndürürse kilidi serbest bırakın.