सूत्र सिंक्रनाइज़ करना
थ्रेड सिंक्रोनाइज़ेशन को एक ऐसी विधि के रूप में परिभाषित किया जा सकता है जिसकी मदद से हमें यह आश्वासन दिया जा सकता है कि दो या अधिक समवर्ती धागे एक साथ महत्वपूर्ण खंड के रूप में जाना जाने वाले प्रोग्राम सेगमेंट तक नहीं पहुंच रहे हैं। दूसरी ओर, जैसा कि हम जानते हैं कि महत्वपूर्ण खंड उस कार्यक्रम का हिस्सा है जहां साझा संसाधन तक पहुँचा जाता है। इसलिए हम कह सकते हैं कि सिंक्रनाइजेशन यह सुनिश्चित करने की प्रक्रिया है कि दो या दो से अधिक धागे एक ही समय में संसाधनों तक पहुंच कर एक-दूसरे के साथ इंटरफेस नहीं करते हैं। नीचे दिए गए आरेख से पता चलता है कि चार धागे एक ही समय में एक कार्यक्रम के महत्वपूर्ण खंड तक पहुंचने की कोशिश कर रहे हैं।
इसे स्पष्ट करने के लिए, दो या अधिक थ्रेड्स को एक ही समय में सूची में ऑब्जेक्ट जोड़ने की कोशिश करें। यह अधिनियम एक सफल अंत का कारण नहीं बन सकता है क्योंकि या तो यह एक या सभी वस्तुओं को छोड़ देगा या यह सूची की स्थिति को पूरी तरह से भ्रष्ट कर देगा। यहां सिंक्रनाइज़ेशन की भूमिका यह है कि एक समय में केवल एक धागा सूची तक पहुंच सकता है।
थ्रेड सिंक्रनाइज़ेशन में समस्याएँ
समवर्ती प्रोग्रामिंग को लागू करते समय या आदिम सिंक्रनाइज़ेशन को लागू करते समय हम समस्याओं का सामना कर सकते हैं। इस भाग में, हम दो प्रमुख मुद्दों पर चर्चा करेंगे। मुद्दे हैं -
- Deadlock
- दौड़ की स्थिति
दौड़ की स्थिति
यह समवर्ती प्रोग्रामिंग में प्रमुख मुद्दों में से एक है। साझा संसाधनों के समवर्ती पहुंच से दौड़ की स्थिति बन सकती है। एक दौड़ की स्थिति को एक ऐसी स्थिति के रूप में परिभाषित किया जा सकता है जब दो या अधिक थ्रेड साझा किए गए डेटा तक पहुंच सकते हैं और फिर उसी समय इसके मूल्य को बदलने की कोशिश कर सकते हैं। इसके कारण, चर के मान अप्रत्याशित हो सकते हैं और प्रक्रियाओं के संदर्भ स्विच के समय के आधार पर भिन्न हो सकते हैं।
उदाहरण
दौड़ की स्थिति की अवधारणा को समझने के लिए इस उदाहरण पर विचार करें -
Step 1 - इस चरण में, हमें सूत्रण मॉड्यूल आयात करने की आवश्यकता है -
import threading
Step 2 - अब, एक वैश्विक चर को परिभाषित करें, मान लें कि x, इसके मान के साथ 0 -
x = 0
Step 3 - अब, हमें परिभाषित करने की आवश्यकता है increment_global() फ़ंक्शन, जो इस वैश्विक फ़ंक्शन x में 1 से वेतन वृद्धि करेगा -
def increment_global():
global x
x += 1
Step 4 - इस चरण में, हम परिभाषित करेंगे taskofThread()फ़ंक्शन, जो समय की एक निर्दिष्ट संख्या के लिए increment_global () फ़ंक्शन को कॉल करेगा; हमारे उदाहरण के लिए यह 50000 गुना है -
def taskofThread():
for _ in range(50000):
increment_global()
Step 5- अब, मुख्य () फ़ंक्शन को परिभाषित करें जिसमें थ्रेड्स t1 और t2 बनाए गए हैं। दोनों को स्टार्ट () फ़ंक्शन की सहायता से शुरू किया जाएगा और जब तक वे जॉइन () फ़ंक्शन की सहायता से अपनी नौकरी समाप्त नहीं करते तब तक प्रतीक्षा करें।
def main():
global x
x = 0
t1 = threading.Thread(target= taskofThread)
t2 = threading.Thread(target= taskofThread)
t1.start()
t2.start()
t1.join()
t2.join()
Step 6- अब, हमें सीमा देने की आवश्यकता है कि हम कितने पुनरावृत्तियों को मुख्य () फ़ंक्शन को कॉल करना चाहते हैं। यहां, हम इसे 5 बार कॉल कर रहे हैं।
if __name__ == "__main__":
for i in range(5):
main()
print("x = {1} after Iteration {0}".format(i,x))
नीचे दिखाए गए आउटपुट में, हम रेस की स्थिति के प्रभाव को देख सकते हैं क्योंकि प्रत्येक पुनरावृत्ति 100000 की उम्मीद के बाद x का मान है। हालांकि, मूल्य में बहुत भिन्नता है। यह साझा वैश्विक चर x के लिए थ्रेड्स के समवर्ती उपयोग के कारण है।
उत्पादन
x = 100000 after Iteration 0
x = 54034 after Iteration 1
x = 80230 after Iteration 2
x = 93602 after Iteration 3
x = 93289 after Iteration 4
ताले का उपयोग कर दौड़ की स्थिति से निपटना
जैसा कि हमने उपरोक्त कार्यक्रम में दौड़ की स्थिति के प्रभाव को देखा है, हमें एक सिंक्रनाइज़ेशन टूल की आवश्यकता है, जो कई थ्रेड्स के बीच दौड़ की स्थिति से निपट सकता है। पायथन में, द<threading>मॉड्यूल रेस की स्थिति से निपटने के लिए लॉक क्लास प्रदान करता है। इसके अलावा,Lockवर्ग विभिन्न विधियाँ प्रदान करता है जिनकी सहायता से हम कई थ्रेड्स के बीच दौड़ की स्थिति को संभाल सकते हैं। विधियाँ नीचे वर्णित हैं -
अधिग्रहण () विधि
इस विधि का उपयोग ताला प्राप्त करने के लिए किया जाता है, अर्थात। निम्नलिखित सही या गलत मूल्य के आधार पर एक लॉक को अवरुद्ध या गैर-अवरुद्ध किया जा सकता है -
With value set to True - यदि अधिग्रहण () विधि ट्रू के साथ लगाई गई है, जो कि डिफ़ॉल्ट तर्क है, तो थ्रेड निष्पादन तब तक अवरुद्ध हो जाता है जब तक कि लॉक अनलॉक न हो जाए।
With value set to False - यदि अधिग्रहित () विधि को गलत के साथ लागू किया जाता है, जो डिफ़ॉल्ट तर्क नहीं है, तो थ्रेड निष्पादन तब तक अवरुद्ध नहीं होता है जब तक कि यह सही पर सेट न हो, अर्थात, जब तक कि यह लॉक न हो।
रिलीज () विधि
इस विधि का उपयोग ताला जारी करने के लिए किया जाता है। इस विधि से संबंधित कुछ महत्वपूर्ण कार्य निम्नलिखित हैं -
यदि कोई ताला बंद है, तो ताला release()विधि यह अनलॉक होगा। इसका काम वास्तव में एक धागे को आगे बढ़ने की अनुमति देना है यदि एक से अधिक धागे अवरुद्ध हैं और लॉक के अनलॉक होने की प्रतीक्षा कर रहे हैं।
यह एक उठाएंगे ThreadError अगर ताला पहले से ही खुला है।
अब, हम दौड़ की स्थिति से बचने के लिए लॉक प्रोग्राम और इसके तरीकों के साथ उपरोक्त कार्यक्रम को फिर से लिख सकते हैं। हमें टास्क लॉफ़्रेड () पद्धति को लॉक तर्क के साथ परिभाषित करने की आवश्यकता है और फिर दौड़ की स्थिति से बचने के लिए लॉक को अवरुद्ध और गैर-अवरुद्ध करने के लिए अधिग्रहण () और रिलीज़ () तरीकों का उपयोग करने की आवश्यकता है।
उदाहरण
नस्ल की स्थिति से निपटने के लिए तालों की अवधारणा को समझने के लिए अजगर कार्यक्रम का उदाहरण निम्नलिखित है -
import threading
x = 0
def increment_global():
global x
x += 1
def taskofThread(lock):
for _ in range(50000):
lock.acquire()
increment_global()
lock.release()
def main():
global x
x = 0
lock = threading.Lock()
t1 = threading.Thread(target = taskofThread, args = (lock,))
t2 = threading.Thread(target = taskofThread, args = (lock,))
t1.start()
t2.start()
t1.join()
t2.join()
if __name__ == "__main__":
for i in range(5):
main()
print("x = {1} after Iteration {0}".format(i,x))
निम्न आउटपुट से पता चलता है कि दौड़ की स्थिति का प्रभाव उपेक्षित है; x के मान के रूप में, प्रत्येक पुनरावृत्ति के बाद, अब 100000 है, जो इस कार्यक्रम की अपेक्षा के अनुसार है।
उत्पादन
x = 100000 after Iteration 0
x = 100000 after Iteration 1
x = 100000 after Iteration 2
x = 100000 after Iteration 3
x = 100000 after Iteration 4
डेडलॉक - द डाइनिंग फिलॉसॉफ़र्स समस्या
डेडलॉक एक परेशानी वाला मुद्दा है जो समवर्ती प्रणालियों को डिजाइन करते समय सामना कर सकता है। हम इस मुद्दे को निम्नानुसार भोजन दार्शनिक समस्या की सहायता से बता सकते हैं -
एड्स्जर डेज्स्ट्रा ने मूल रूप से डाइनिंग दार्शनिक समस्या पेश की, जो गतिरोध नामक समवर्ती प्रणाली की सबसे बड़ी समस्या में से एक के प्रसिद्ध चित्रण में से एक है।
इस समस्या में, एक राउंड टेबल पर पांच प्रसिद्ध दार्शनिक बैठे हैं जो अपने कटोरे से कुछ खा रहे हैं। पांच कांटे हैं जिनका उपयोग पांच दार्शनिक अपने भोजन को खाने के लिए कर सकते हैं। हालांकि, दार्शनिक अपने भोजन को खाने के लिए एक ही समय में दो कांटे का उपयोग करने का निर्णय लेते हैं।
अब, दार्शनिकों के लिए दो मुख्य स्थितियां हैं। पहला, दार्शनिकों में से प्रत्येक या तो खाने या सोच की स्थिति में हो सकता है और दूसरा, उन्हें पहले दोनों कांटे यानी बाएं और दाएं प्राप्त करने होंगे। मुद्दा तब उठता है जब पांच दार्शनिकों में से प्रत्येक एक ही समय में बाएं कांटा लेने का प्रबंधन करता है। अब वे सभी स्वतंत्र कांटे के मुक्त होने की प्रतीक्षा कर रहे हैं, लेकिन वे अपने कांटे को तब तक नहीं छोड़ेंगे, जब तक वे अपना भोजन नहीं खा लेते हैं और सही कांटा कभी उपलब्ध नहीं होगा। इसलिए, खाने की मेज पर गतिरोध की स्थिति होगी।
समवर्ती प्रणाली में गतिरोध
अब अगर हम देखें तो हमारे समवर्ती प्रणालियों में भी यही मुद्दा उठ सकता है। उपरोक्त उदाहरण में कांटे सिस्टम संसाधन होंगे और प्रत्येक दार्शनिक प्रक्रिया का प्रतिनिधित्व कर सकता है, जो संसाधनों को प्राप्त करने के लिए प्रतिस्पर्धा कर रहा है।
पायथन कार्यक्रम के साथ समाधान
दार्शनिकों को दो प्रकारों में विभाजित करके इस समस्या का हल खोजा जा सकता है - greedy philosophers तथा generous philosophers। मुख्य रूप से एक लालची दार्शनिक बाएं कांटा को लेने की कोशिश करेगा और तब तक इंतजार करेगा जब तक वह वहां न हो। वह तब सही कांटा होने का इंतजार करेगा, उसे उठाएगा, खाएगा और फिर नीचे रख देगा। दूसरी ओर, एक उदार दार्शनिक बाएं कांटे को लेने की कोशिश करेगा और अगर यह नहीं है, तो वह इंतजार करेगा और कुछ समय बाद फिर से प्रयास करेगा। यदि उन्हें बाएं कांटा मिलता है, तो वे दाएं पाने की कोशिश करेंगे। अगर उन्हें सही कांटा भी मिल जाएगा तो वे दोनों कांटे खा लेंगे और छोड़ देंगे। हालांकि, अगर उन्हें सही कांटा नहीं मिलेगा, तो वे बाएं कांटा जारी करेंगे।
उदाहरण
निम्नलिखित पायथन कार्यक्रम हमें भोजन दार्शनिक समस्या का समाधान खोजने में मदद करेगा -
import threading
import random
import time
class DiningPhilosopher(threading.Thread):
running = True
def __init__(self, xname, Leftfork, Rightfork):
threading.Thread.__init__(self)
self.name = xname
self.Leftfork = Leftfork
self.Rightfork = Rightfork
def run(self):
while(self.running):
time.sleep( random.uniform(3,13))
print ('%s is hungry.' % self.name)
self.dine()
def dine(self):
fork1, fork2 = self.Leftfork, self.Rightfork
while self.running:
fork1.acquire(True)
locked = fork2.acquire(False)
if locked: break
fork1.release()
print ('%s swaps forks' % self.name)
fork1, fork2 = fork2, fork1
else:
return
self.dining()
fork2.release()
fork1.release()
def dining(self):
print ('%s starts eating '% self.name)
time.sleep(random.uniform(1,10))
print ('%s finishes eating and now thinking.' % self.name)
def Dining_Philosophers():
forks = [threading.Lock() for n in range(5)]
philosopherNames = ('1st','2nd','3rd','4th', '5th')
philosophers= [DiningPhilosopher(philosopherNames[i], forks[i%5], forks[(i+1)%5]) \
for i in range(5)]
random.seed()
DiningPhilosopher.running = True
for p in philosophers: p.start()
time.sleep(30)
DiningPhilosopher.running = False
print (" It is finishing.")
Dining_Philosophers()
उपरोक्त कार्यक्रम लालची और उदार दार्शनिकों की अवधारणा का उपयोग करता है। कार्यक्रम का उपयोग भी किया हैacquire() तथा release() के तरीके Lock की कक्षा <threading>मापांक। हम निम्न आउटपुट में समाधान देख सकते हैं -
उत्पादन
4th is hungry.
4th starts eating
1st is hungry.
1st starts eating
2nd is hungry.
5th is hungry.
3rd is hungry.
1st finishes eating and now thinking.3rd swaps forks
2nd starts eating
4th finishes eating and now thinking.
3rd swaps forks5th starts eating
5th finishes eating and now thinking.
4th is hungry.
4th starts eating
2nd finishes eating and now thinking.
3rd swaps forks
1st is hungry.
1st starts eating
4th finishes eating and now thinking.
3rd starts eating
5th is hungry.
5th swaps forks
1st finishes eating and now thinking.
5th starts eating
2nd is hungry.
2nd swaps forks
4th is hungry.
5th finishes eating and now thinking.
3rd finishes eating and now thinking.
2nd starts eating 4th starts eating
It is finishing.