Yanıtlar:
Alındığı http://en.wikipedia.org/wiki/Deadlock :
Eşzamanlı hesaplamada, kilitlenme , bir grup eylemin her üyesinin, başka bir üyenin bir kilit açmasını beklediği bir durumdur
Bir livelock livelock katılan süreçlerin devletler sürekli birbirlerine ilişkin, hiçbiri ilerleyen ile değiştirmek dışında çıkmaza benzer. Livelock özel bir kaynak açlığı vakasıdır; genel tanım sadece belirli bir sürecin ilerlemediğini belirtir.
Gerçek dünyadaki canlı kilit örneği, iki kişi dar bir koridorda buluştuğunda ortaya çıkar ve her biri, diğerinin geçmesine izin vermek için bir kenara hareket ederek kibar olmaya çalışır, ancak her ikisi de tekrar tekrar hareket etmeden herhangi bir ilerleme olmadan yan yana sallanırlar. aynı şekilde.
Livelock, kilitlenmeyi algılayan ve kurtarmayı engelleyen bazı algoritmalar için bir risktir. Birden fazla işlem harekete geçerse, kilitlenme algılama algoritması tekrar tekrar tetiklenebilir. Bu, yalnızca bir işlemin (rastgele veya öncelikli olarak seçilen) harekete geçmesini sağlayarak önlenebilir.
Bir iş parçacığı genellikle başka bir iş parçacığının eylemine yanıt olarak çalışır. Diğer iş parçacığının eylemi de başka bir iş parçacığının eylemine bir yanıtsa, livelock oluşabilir.
Kilitlenmede olduğu gibi, kilitlenmemiş iş parçacıkları daha fazla ilerleme kaydedemez . Ancak, iş parçacıkları engellenmez - işi sürdürmek için birbirlerine yanıt vermekle meşguller . Bu, bir koridorda birbirini geçmeye çalışan iki kişi ile karşılaştırılabilir: Alphonse, Gaston'un geçmesine izin vermek için sola hareket ederken Gaston, Alphonse'un geçmesine izin vermek için sağa hareket eder. Hala birbirlerini bloke ettiklerini gören Alphonse sağa, Gaston sola hareket ediyor. Hala birbirlerini engelliyorlar, vb.
Canlı kilit ve kilitlenme arasındaki temel fark, evrelerin engellenmeyecekleri, bunun yerine sürekli olarak yanıt vermeye çalışacaklarıdır.
Bu görüntüde, her iki daire (iş parçacıkları veya işlemler) sola ve sağa hareket ederek diğerine yer vermeye çalışacaktır. Ama daha fazla ilerleyemezler.
Buradaki tüm içerik ve örnekler
İşletim Sistemleri: İç ve Tasarım İlkeleri
William Stallings
8º Edition
Kilitlenme : İki veya daha fazla işlemin devam edemediği bir durumdur, çünkü her biri diğerinin bir şey yapmasını beklemektedir.
Örneğin, iki işlemi düşünün, P1 ve P2 ve iki kaynağı, R1 ve R2. Her işlemin, işlevinin bir bölümünü gerçekleştirmek için her iki kaynağa da erişmesi gerektiğini varsayalım. Daha sonra aşağıdaki duruma sahip olmak mümkündür: OS, R1'i P2'ye ve R2'yi P1'i atar. Her süreç iki kaynaktan birini bekliyor. İkisi de, diğer kaynağı edinene ve her iki kaynağı gerektiren işlevi yerine getirene kadar zaten sahip olduğu kaynağı serbest bırakmaz. İki süreç kilitlendi
Livelock : İki veya daha fazla sürecin yararlı bir iş yapmadan diğer süreç (ler) deki değişikliklere karşılık olarak durumlarını sürekli olarak değiştirdiği bir durum:
Açlık : Çalıştırılabilir bir sürecin zamanlayıcı tarafından süresiz olarak gözden kaçırıldığı bir durum; ilerleyebilmesine rağmen, asla seçilmez.
Üç işlemin (P1, P2, P3) her birinin R kaynağına periyodik erişim gerektirdiğini varsayalım. P1'in kaynağa sahip olduğu durumu ve hem P2 hem de P3'ün geciktiğini ve bu kaynağı beklediğini düşünün. P1 kritik bölümünden çıktığında, P2 veya P3'ün R'ye erişmesine izin verilmelidir. İşletim sisteminin P3'e erişim izni verdiğini ve P3'ün kritik bölümünü tamamlamadan önce P1'in tekrar erişim gerektirdiğini varsayın. İşletim sistemi, P3 bittikten sonra P1'e erişim izni verirse ve daha sonra alternatif olarak P1 ve P3'e erişim izni verirse, herhangi bir kilitlenme durumu olmasa bile P2'nin kaynağa erişimi sınırsız olarak reddedilebilir.
EK A - BİRLEŞİKTE KONULAR
Kilitlenme Örneği
Her iki işlem de while deyimini yürütmeden önce bayraklarını true değerine ayarlarsa, her biri diğerinin kritik bölümüne girdiğini ve kilitlenmeye neden olduğunu düşünür.
/* PROCESS 0 */
flag[0] = true; // <- get lock 0
while (flag[1]) // <- is lock 1 free?
/* do nothing */; // <- no? so I wait 1 second, for example
// and test again.
// on more sophisticated setups we can ask
// to be woken when lock 1 is freed
/* critical section*/; // <- do what we need (this will never happen)
flag[0] = false; // <- releasing our lock
/* PROCESS 1 */
flag[1] = true;
while (flag[0])
/* do nothing */;
/* critical section*/;
flag[1] = false;
Livelock Örneği
/* PROCESS 0 */
flag[0] = true; // <- get lock 0
while (flag[1]){
flag[0] = false; // <- instead of sleeping, we do useless work
// needed by the lock mechanism
/*delay */; // <- wait for a second
flag[0] = true; // <- and restart useless work again.
}
/*critical section*/; // <- do what we need (this will never happen)
flag[0] = false;
/* PROCESS 1 */
flag[1] = true;
while (flag[0]) {
flag[1] = false;
/*delay */;
flag[1] = true;
}
/* critical section*/;
flag[1] = false;
[...] aşağıdaki olay sırasını göz önünde bulundurun:
Bu dizi süresiz olarak uzatılabilir ve her iki süreç de kritik bölümüne giremez. Açıkçası, bu bir kilitlenme değildir , çünkü iki sürecin nispi hızında herhangi bir değişiklik bu döngüyü kıracak ve kritik bölüme girmeye izin verecektir. Bu duruma livelock adı verilir . Kilitlenmenin bir dizi işlem kritik bölümlerine girmek istediğinde gerçekleştiğini ancak hiçbir işlemin başarılı olamayacağını hatırlayın. Livelock ile başarılı olan olası yürütme dizileri vardır, ancak hiçbir sürecin kritik bölümüne girmediği bir veya daha fazla yürütme dizisini tanımlamak da mümkündür.
Artık kitaptan içerik değil.
Peki ya spinlocks?
Spinlock, OS kilit mekanizmasının maliyetinden kaçınmak için bir tekniktir. Genellikle şunları yaparsınız:
try
{
lock = beginLock();
doSomething();
}
finally
{
endLock();
}
beginLock()
Maliyet çok daha fazla olduğunda bir sorun ortaya çıkmaya başlar doSomething()
. Çok abartılı terimlerle, beginLock
maliyetlerin 1 saniye, ancak doSomething
sadece 1 milisaniyede ne olacağını hayal edin .
Bu durumda 1 milisaniye beklerseniz, 1 saniye boyunca engellenmekten kaçınabilirsiniz.
Neden beginLock
bu kadar pahalıya mal olacak? Kilit ücretsizse çok pahalı değilse (bkz. Https://stackoverflow.com/a/49712993/5397116 ), ancak kilit ücretsiz değilse işletim sistemi iş parçacığınızı "dondurur", sizi uyandırmak için bir mekanizma kurun kilit serbest olduğunda, ve sonra gelecekte tekrar uyanmak.
Tüm bunlar kilidi kontrol eden bazı döngülerden çok daha pahalıdır. Bu yüzden bazen bir "spinlock" yapmak daha iyidir.
Örneğin:
void beginSpinLock(lock)
{
if(lock) loopFor(1 milliseconds);
else
{
lock = true;
return;
}
if(lock) loopFor(2 milliseconds);
else
{
lock = true;
return;
}
// important is that the part above never
// cause the thread to sleep.
// It is "burning" the time slice of this thread.
// Hopefully for good.
// some implementations fallback to OS lock mechanism
// after a few tries
if(lock) return beginLock(lock);
else
{
lock = true;
return;
}
}
Uygulamanız dikkatli değilse, tüm CPU'yu kilit mekanizmasına harcayarak livelock'a düşebilirsiniz.
Ayrıca bakınız:
https://preshing.com/20120226/roll-your-own-lightweight-mutex/
Döndürme kilidi uygulamam doğru ve optimum mu?
Özet :
Kilitlenme : kimsenin ilerlemediği, hiçbir şey yapmaması (uyku, bekleme vb.) Durum. CPU kullanımı düşük olacaktır;
Livelock : kimsenin ilerlemediği durum, ancak CPU hesaplamaya değil kilit mekanizmasına harcanır;
Açlık: Bir işleyicinin asla kaçma şansı bulamadığı durum; saf kötü şans ya da mülkünün bir kısmı tarafından (örneğin düşük öncelikli);
Spinlock : Kilidin serbest bırakılmasını bekleyen maliyetlerden kaçınma tekniği.
DEADLOCK Deadlock, bir görevin asla tatmin edilemeyen koşulları süresiz olarak beklediği bir durumdur - görev paylaşılan kaynaklar üzerinde özel kontrol talep eder - görev diğer kaynakların serbest bırakılmasını beklerken kaynakları tutar - görevler kaynakları ayırt etmek zorunda bırakılamaz - dairesel bir bekleme durum var
LIVELOCK Livelock koşulları, iki veya daha fazla görev bağımlı olduğunda ve bu kaynakların sonsuza kadar çalışmaya devam ettiği dairesel bağımlılık koşuluna neden olan bazı kaynakları kullandığında ortaya çıkabilir, böylece tüm düşük öncelikli görevlerin çalışmasını engeller (bu düşük öncelikli görevler açlık denen bir durum yaşar)
Belki bu iki örnek size bir kilitlenme ve bir kilitlenme arasındaki farkı göstermektedir:
Bir kilitlenme için Java Örneği:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class DeadlockSample {
private static final Lock lock1 = new ReentrantLock(true);
private static final Lock lock2 = new ReentrantLock(true);
public static void main(String[] args) {
Thread threadA = new Thread(DeadlockSample::doA,"Thread A");
Thread threadB = new Thread(DeadlockSample::doB,"Thread B");
threadA.start();
threadB.start();
}
public static void doA() {
System.out.println(Thread.currentThread().getName() + " : waits for lock 1");
lock1.lock();
System.out.println(Thread.currentThread().getName() + " : holds lock 1");
try {
System.out.println(Thread.currentThread().getName() + " : waits for lock 2");
lock2.lock();
System.out.println(Thread.currentThread().getName() + " : holds lock 2");
try {
System.out.println(Thread.currentThread().getName() + " : critical section of doA()");
} finally {
lock2.unlock();
System.out.println(Thread.currentThread().getName() + " : does not hold lock 2 any longer");
}
} finally {
lock1.unlock();
System.out.println(Thread.currentThread().getName() + " : does not hold lock 1 any longer");
}
}
public static void doB() {
System.out.println(Thread.currentThread().getName() + " : waits for lock 2");
lock2.lock();
System.out.println(Thread.currentThread().getName() + " : holds lock 2");
try {
System.out.println(Thread.currentThread().getName() + " : waits for lock 1");
lock1.lock();
System.out.println(Thread.currentThread().getName() + " : holds lock 1");
try {
System.out.println(Thread.currentThread().getName() + " : critical section of doB()");
} finally {
lock1.unlock();
System.out.println(Thread.currentThread().getName() + " : does not hold lock 1 any longer");
}
} finally {
lock2.unlock();
System.out.println(Thread.currentThread().getName() + " : does not hold lock 2 any longer");
}
}
}
Örnek çıktı:
Thread A : waits for lock 1
Thread B : waits for lock 2
Thread A : holds lock 1
Thread B : holds lock 2
Thread B : waits for lock 1
Thread A : waits for lock 2
Canlı kilit için Java örneği:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LivelockSample {
private static final Lock lock1 = new ReentrantLock(true);
private static final Lock lock2 = new ReentrantLock(true);
public static void main(String[] args) {
Thread threadA = new Thread(LivelockSample::doA, "Thread A");
Thread threadB = new Thread(LivelockSample::doB, "Thread B");
threadA.start();
threadB.start();
}
public static void doA() {
try {
while (!lock1.tryLock()) {
System.out.println(Thread.currentThread().getName() + " : waits for lock 1");
Thread.sleep(100);
}
System.out.println(Thread.currentThread().getName() + " : holds lock 1");
try {
while (!lock2.tryLock()) {
System.out.println(Thread.currentThread().getName() + " : waits for lock 2");
Thread.sleep(100);
}
System.out.println(Thread.currentThread().getName() + " : holds lock 2");
try {
System.out.println(Thread.currentThread().getName() + " : critical section of doA()");
} finally {
lock2.unlock();
System.out.println(Thread.currentThread().getName() + " : does not hold lock 2 any longer");
}
} finally {
lock1.unlock();
System.out.println(Thread.currentThread().getName() + " : does not hold lock 1 any longer");
}
} catch (InterruptedException e) {
// can be ignored here for this sample
}
}
public static void doB() {
try {
while (!lock2.tryLock()) {
System.out.println(Thread.currentThread().getName() + " : waits for lock 2");
Thread.sleep(100);
}
System.out.println(Thread.currentThread().getName() + " : holds lock 2");
try {
while (!lock1.tryLock()) {
System.out.println(Thread.currentThread().getName() + " : waits for lock 1");
Thread.sleep(100);
}
System.out.println(Thread.currentThread().getName() + " : holds lock 1");
try {
System.out.println(Thread.currentThread().getName() + " : critical section of doB()");
} finally {
lock1.unlock();
System.out.println(Thread.currentThread().getName() + " : does not hold lock 1 any longer");
}
} finally {
lock2.unlock();
System.out.println(Thread.currentThread().getName() + " : does not hold lock 2 any longer");
}
} catch (InterruptedException e) {
// can be ignored here for this sample
}
}
}
Örnek çıktı:
Thread B : holds lock 2
Thread A : holds lock 1
Thread A : waits for lock 2
Thread B : waits for lock 1
Thread B : waits for lock 1
Thread A : waits for lock 2
Thread A : waits for lock 2
Thread B : waits for lock 1
Thread B : waits for lock 1
Thread A : waits for lock 2
Thread A : waits for lock 2
Thread B : waits for lock 1
...
Her iki örnek de iplikleri kilitleri farklı sıralarda almaya zorlar. Kilitlenme diğer kilidi beklerken, livelock gerçekten beklemez - umutsuzca kilidi alma şansı olmadan kilidi almaya çalışır. Her deneme CPU döngülerini tüketir.
A iş parçacığı ve B iş parçacığı olduğunu düşünün. Her ikisi synchronised
de aynı nesnede ve bu bloğun içinde her ikisinin de güncellediği genel bir değişken var;
static boolean commonVar = false;
Object lock = new Object;
...
void threadAMethod(){
...
while(commonVar == false){
synchornized(lock){
...
commonVar = true
}
}
}
void threadBMethod(){
...
while(commonVar == true){
synchornized(lock){
...
commonVar = false
}
}
}
İplik A girdiğinde Yani, while
döngü ve kilit tutan, o şekilde davranmak ve set neler yapar commonVar
için true
. Sonra B, gelir de girer iplik while
döngü ve yana commonVar
olduğunu true
şimdi, kiliti muktedir olduğunu. Bunu yapar, synchronised
bloğu yürütür ve commonVar
geri ayarlanır false
. Şimdi, iplik A yine 's yeni işlemci pencere alır, bu oldu bırakmak üzereyken while
döngü ama iplik B sadece geri onu belirledi false
döngü tekrarlanır böylece tekrar tekrar,. İplikler bir şey yaparlar (geleneksel anlamda engellenmezler) ama hemen hemen hiçbir şey için.
Livelock'un burada görünmesi gerekmediğini belirtmek de güzel olabilir. synchronised
Blok bittikten sonra zamanlayıcı diğer iş parçacığı tercih varsayıyorum . Çoğu zaman, bunun zor bir beklenti olduğunu ve kaputun altında gerçekleşen birçok şeye bağlı olduğunu düşünüyorum.