जवाबों:
Http://en.wikipedia.org/wiki/Deadlock से लिया गया :
समवर्ती कंप्यूटिंग में, एक गतिरोध वह स्थिति होती है, जिसमें क्रियाओं के समूह का प्रत्येक सदस्य, किसी अन्य सदस्य द्वारा लॉक भेजने के लिए प्रतीक्षा कर रहा होता है
एक लाइवलॉक गतिरोध के समान है, सिवाय इसके कि लाइवलॉक में शामिल प्रक्रियाओं की स्थिति एक दूसरे के संबंध में लगातार बदलती रहती है, कोई भी प्रगति नहीं करता है। लाइवलॉक संसाधन भुखमरी का एक विशेष मामला है; सामान्य परिभाषा केवल यह बताती है कि एक विशिष्ट प्रक्रिया प्रगति नहीं कर रही है।
लाइवलॉक का एक वास्तविक दुनिया उदाहरण तब होता है जब दो लोग एक संकीर्ण गलियारे में मिलते हैं, और प्रत्येक दूसरे को जाने देने के लिए अलग हटकर विनम्र होने की कोशिश करता है, लेकिन वे बिना किसी प्रगति के साइड-साइड से बहते हुए समाप्त होते हैं क्योंकि वे दोनों बार-बार चलते हैं उसी समय उसी तरह।
लाइवलॉक कुछ एल्गोरिदम के साथ एक जोखिम है जो गतिरोध का पता लगाता है और पुनर्प्राप्त करता है। यदि एक से अधिक प्रक्रिया होती है, तो गतिरोध का पता लगाने वाले एल्गोरिदम को बार-बार ट्रिगर किया जा सकता है। यह सुनिश्चित करने से बचा जा सकता है कि केवल एक प्रक्रिया (यादृच्छिक रूप से या प्राथमिकता से चुनी गई) कार्रवाई करती है।
एक धागा अक्सर दूसरे धागे की कार्रवाई के जवाब में कार्य करता है। यदि दूसरे धागे की कार्रवाई दूसरे धागे की कार्रवाई की प्रतिक्रिया भी है, तो लाइवलॉक का परिणाम हो सकता है।
गतिरोध के साथ, livelocked थ्रेड आगे प्रगति करने में असमर्थ हैं । हालांकि, थ्रेड्स अवरुद्ध नहीं हैं - वे काम को फिर से शुरू करने के लिए बस एक-दूसरे को जवाब देने में व्यस्त हैं । यह गलियारे में एक-दूसरे को पारित करने का प्रयास करने वाले दो लोगों की तुलना में है: अल्फोंस गैस्टन को जाने देने के लिए अपने बाएं ओर जाता है, जबकि गैस्टन अल्फोंस को जाने देने के लिए अपने दाहिने ओर जाता है। यह देखते हुए कि वे अभी भी एक-दूसरे को रोक रहे हैं, अल्फोंस अपने दाहिने ओर जाता है, जबकि गैस्टन अपने बाएं ओर जाता है। वे अभी भी एक-दूसरे को रोक रहे हैं, और इसी तरह ...
लाइवलॉक और गतिरोध के बीच मुख्य अंतर यह है कि धागे अवरुद्ध नहीं होने जा रहे हैं, इसके बजाय वे एक-दूसरे को लगातार जवाब देने की कोशिश करेंगे।
इस छवि में, दोनों वृत्त (धागे या प्रक्रियाएँ) बाएँ और दाएँ घुमाकर दूसरे को स्थान देने का प्रयास करेंगे। लेकिन वे आगे नहीं बढ़ सकते।
यहां सभी सामग्री और उदाहरण हैं
ऑपरेटिंग सिस्टम: इंटर्नल्स और डिज़ाइन सिद्धांत
विलियम स्टालिंग्स
8। संस्करण
गतिरोध : एक ऐसी स्थिति जिसमें दो या दो से अधिक प्रक्रियाएं आगे नहीं बढ़ पाती हैं क्योंकि प्रत्येक व्यक्ति कुछ करने के लिए एक दूसरे की प्रतीक्षा कर रहा है।
उदाहरण के लिए, दो प्रक्रियाओं, पी 1 और पी 2 और दो संसाधनों, आर 1 और आर 2 पर विचार करें। मान लीजिए कि प्रत्येक प्रक्रिया को अपने कार्य का हिस्सा करने के लिए दोनों संसाधनों तक पहुंच की आवश्यकता होती है। तब निम्न स्थिति होना संभव है: ओएस आर 1 को पी 2, और आर 2 को पी 1 प्रदान करता है। प्रत्येक प्रक्रिया दो संसाधनों में से एक की प्रतीक्षा कर रही है। न तो उस संसाधन को जारी करेगा जो पहले से ही मालिक है जब तक कि उसने दूसरे संसाधन का अधिग्रहण नहीं किया है और दोनों संसाधनों की आवश्यकता वाले फ़ंक्शन का प्रदर्शन किया है। दो प्रक्रियाएं गतिरोध हैं
Livelock : एक ऐसी स्थिति जिसमें दो या दो से अधिक प्रक्रियाएं लगातार किसी भी उपयोगी कार्य को किए बिना अन्य प्रक्रियाओं (तों) में परिवर्तन के जवाब में अपने राज्यों को लगातार बदलती रहती हैं:
भुखमरी : एक ऐसी स्थिति जिसमें शेड्यूलर द्वारा एक चलने योग्य प्रक्रिया को अनिश्चित काल तक अनदेखा किया जाता है; हालांकि यह आगे बढ़ने में सक्षम है, लेकिन इसे कभी नहीं चुना जाता है।
मान लीजिए कि तीन प्रक्रियाओं (पी 1, पी 2, पी 3) में से प्रत्येक को संसाधन आर तक आवधिक पहुंच की आवश्यकता होती है। उस स्थिति पर विचार करें जिसमें पी 1 संसाधन के कब्जे में है, और पी 2 और पी 3 दोनों में देरी हो रही है, उस संसाधन की प्रतीक्षा कर रहे हैं। जब पी 1 अपने महत्वपूर्ण खंड से बाहर निकलता है, तो पी 2 या पी 3 को आर। तक पहुंच की अनुमति दी जानी चाहिए। मान लें कि ओएस पी 3 तक पहुंचता है और पी 1 को फिर से अपने महत्वपूर्ण खंड को पूरा करने से पहले एक्सेस की आवश्यकता होती है। यदि पी 3 समाप्त होने के बाद ओएस पी 1 तक पहुंच जाता है, और बाद में वैकल्पिक रूप से पी 1 और पी 3 तक पहुंच प्रदान करता है, तो पी 2 अनिश्चितकालीन संसाधन तक पहुंच से इनकार कर सकता है, भले ही कोई गतिरोध की स्थिति न हो।
परिशिष्ट ए - सामंजस्य में विषय
डेडलॉक उदाहरण
यदि दोनों प्रक्रियाओं ने अपने झंडे को सच में सेट किया है, तो या तो पहले बयान को निष्पादित किया है, तो प्रत्येक सोचेंगे कि दूसरे ने अपने महत्वपूर्ण खंड में प्रवेश किया है, जिससे गतिरोध पैदा हो गया है।
/* 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;
लाइवलॉक उदाहरण
/* 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;
[...] घटनाओं के निम्नलिखित अनुक्रम पर विचार करें:
इस क्रम को अनिश्चित काल तक बढ़ाया जा सकता है, और न ही प्रक्रिया अपने महत्वपूर्ण खंड में प्रवेश कर सकती है। सख्ती से बोलना, यह गतिरोध नहीं है , क्योंकि दो प्रक्रियाओं की सापेक्ष गति में कोई भी परिवर्तन इस चक्र को तोड़ देगा और एक को महत्वपूर्ण अनुभाग में प्रवेश करने की अनुमति देगा। इस स्थिति को लाइवलॉक के रूप में संदर्भित किया जाता है । याद रखें कि गतिरोध तब होता है जब प्रक्रियाओं का एक सेट उनके महत्वपूर्ण वर्गों में प्रवेश करना चाहता है लेकिन कोई भी प्रक्रिया सफल नहीं हो सकती है। लाइवलॉक के साथ , निष्पादन के संभावित अनुक्रम होते हैं जो सफल होते हैं, लेकिन एक या एक से अधिक निष्पादन अनुक्रमों का वर्णन करना भी संभव है, जिसमें कोई भी प्रक्रिया कभी भी अपने महत्वपूर्ण खंड में प्रवेश नहीं करती है।
अब किताब से सामग्री नहीं।
और पालक के बारे में क्या?
स्पिनलॉक ओएस लॉक तंत्र की लागत से बचने की एक तकनीक है। आमतौर पर आप ऐसा करेंगे:
try
{
lock = beginLock();
doSomething();
}
finally
{
endLock();
}
एक समस्या तब सामने आने लगती है जब beginLock()
लागत बहुत अधिक होती है doSomething()
। बहुत ही एक्सटर्स्ड शब्दों में, कल्पना करें कि जब beginLock
लागत 1 सेकंड होती है, लेकिन doSomething
लागत केवल 1 मिलीसेकंड होती है।
इस मामले में यदि आपने 1 मिली सेकंड इंतजार किया, तो आप 1 सेकंड के लिए बाधा बनने से बचेंगे।
beginLock
इतना खर्च क्यों होगा? यदि लॉक मुफ़्त है, तो बहुत खर्च नहीं होता है ( https://stackoverflow.com/a/49712993/5397116 देखें ), लेकिन अगर लॉक मुफ़्त नहीं है, तो OS आपके धागे को "फ्रीज़" करेगा, आपको जगाने के लिए एक तंत्र तैयार करेगा। जब ताला मुक्त हो जाता है, और फिर भविष्य में आपको फिर से जगाता है।
यह सब लॉक की जाँच करने वाले कुछ लूपों की तुलना में बहुत अधिक महंगा है। यही कारण है कि कभी-कभी "स्पिनलॉक" करना बेहतर होता है।
उदाहरण के लिए:
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;
}
}
यदि आपका कार्यान्वयन सावधान नहीं है, तो आप लॉक सिस्टम पर सभी सीपीयू खर्च करके लाइवलॉक पर गिर सकते हैं।
और देखें:
https://preshing.com/20120226/roll-your-own-lightweight-mutex/
क्या मेरा स्पिन लॉक कार्यान्वयन सही और इष्टतम है?
सारांश :
गतिरोध : वह स्थिति जहाँ कोई प्रगति नहीं करता, कुछ भी नहीं कर रहा (सो रहा है, प्रतीक्षा कर रहा है ..)। सीपीयू का उपयोग कम होगा;
लाइवलॉक : ऐसी स्थिति जहां कोई भी प्रगति नहीं करता है, लेकिन सीपीयू लॉक तंत्र पर खर्च किया जाता है और आपकी गणना पर नहीं;
भुखमरी: वह स्थिति जहां एक प्रचारक को कभी भी दौड़ने का मौका नहीं मिलता; शुद्ध दुर्भाग्य से या इसकी संपत्ति में से कुछ (कम प्राथमिकता, उदाहरण के लिए);
स्पिनलॉक : लागत से बचने की तकनीक ताला मुक्त होने की प्रतीक्षा कर रही है।
DEADLOCK डेडलॉक एक ऐसी स्थिति है जिसमें कोई कार्य उन शर्तों के लिए अनिश्चित काल तक प्रतीक्षा करता है जो कभी संतुष्ट नहीं हो सकते हैं - कार्य साझा संसाधनों पर अनन्य नियंत्रण का दावा करता है - कार्य संसाधनों को जारी रखते हुए अन्य संसाधनों की प्रतीक्षा करता है - कार्य को संसाधनों को त्यागने के लिए मजबूर नहीं किया जा सकता - एक परिपत्र प्रतीक्षा स्थिति मौजूद है
LIVELOCK लाइवलॉक की स्थिति तब उत्पन्न हो सकती है जब दो या दो से अधिक कार्य निर्भर करते हैं और कुछ संसाधन का उपयोग करते हुए एक परिपत्र निर्भरता की स्थिति पैदा करते हैं, जहाँ वे कार्य हमेशा चलते रहते हैं, इस प्रकार सभी निचले प्राथमिकता स्तर के कार्यों को चलने से रोकते हैं (ये निम्न प्राथमिकता वाले कार्य एक स्थिति को भुखमरी कहते हैं)
हो सकता है कि ये दो उदाहरण आपको एक गतिरोध और लाइवलॉक के बीच के अंतर को दर्शाते हैं:
गतिरोध के लिए जावा-उदाहरण:
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");
}
}
}
नमूना उत्पादन:
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
एक लाइवलॉक के लिए जावा-उदाहरण:
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
}
}
}
नमूना उत्पादन:
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
...
दोनों उदाहरण थ्रेड्स को अलग-अलग क्रम में ताले को जलाने के लिए मजबूर करते हैं। जबकि गतिरोध दूसरे लॉक का इंतजार करता है, लाइवलॉक वास्तव में इंतजार नहीं करता है - यह सख्त मौका प्राप्त करने के बिना लॉक को प्राप्त करने की कोशिश करता है। हर कोशिश में सीपीयू साइकिल की खपत होती है।
कल्पना कीजिए कि आपने थ्रेड ए और थ्रेड बी हैं। वे दोनों synchronised
एक ही वस्तु पर हैं और इस ब्लॉक के अंदर एक वैश्विक चर है जो वे दोनों अपडेट कर रहे हैं;
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
}
}
}
इसलिए, जब थ्रेड ए while
लूप में प्रवेश करता है और लॉक रखता है, तो वह वही करता है जो उसे करना है और सेट करना commonVar
है true
। तब थ्रेड बी, में आता है में प्रवेश करती है while
पाश और के बाद से commonVar
है true
अब, यह ताला पकड़ करने में सक्षम हो सकता है। ऐसा करता है, synchronised
ब्लॉक निष्पादित करता है , और commonVar
वापस सेट करता है false
। अब, थ्रेड ए को फिर से नई सीपीयू विंडो मिलती है, यह लूप छोड़ने के बारे में थीwhile
लेकिन थ्रेड बी ने इसे वापस सेट कर दिया है false
, इसलिए यह चक्र फिर से दोहराता है। धागे कुछ करते हैं (इसलिए वे पारंपरिक अर्थों में अवरुद्ध नहीं हैं) लेकिन बहुत ज्यादा कुछ नहीं के लिए।
शायद यह बताना भी अच्छा होगा कि लाइवलॉक को यहां प्रदर्शित होना जरूरी नहीं है। मैं मान रहा हूं कि synchronised
ब्लॉक खत्म होने के बाद शेड्यूलर दूसरे धागे का पक्षधर है । ज्यादातर समय, मुझे लगता है कि यह एक कठिन-हिट की उम्मीद है और हुड के तहत होने वाली कई चीजों पर निर्भर करता है।