Semaphore एक सिंक्रोनाइज़ेशन प्रिमिटिव है जो काउंटर और प्रतीक्षा कर रहे थ्रेड्स की कतार के माध्यम से एक साझा संसाधन तक पहुँच को नियंत्रित करता है। Wikipedia, 2024 के अनुसार, सेमाफोर को एड्सगर डेक्सट्रा ने 1965 में मल्टीथ्रेडेड इंटरैक्शन समस्याओं को हल करने के लिए प्रस्तावित किया था। यह उपकरण क्रिटिकल सेक्शन के साथ एक साथ काम करने वाले थ्रेड्स की संख्या को सीमित करने की अनुमति देता है।
मुख्य बिंदु
Semaphore एक सिंक्रोनाइज़ेशन प्रिमिटिव है जो एक साझा संसाधन तक पहुँच को नियंत्रित करने के लिए काउंटर का उपयोग करता है। यह अवधारणा एड्सगर डेक्सट्रा द्वारा 1965 में प्रस्तावित की गई थी और ऑपरेटिंग सिस्टम में सभी आधुनिक सिंक्रोनाइज़ेशन तंत्रों की नींव बनी।
सेमाफोर दो परमाणु संक्रियाओं वाला एक पूर्णांक चर है: wait (acquire) और signal (release)। wait संक्रिया काउंटर को घटाती है, जबकि signal इसे बढ़ाती है। जब काउंटर शून्य तक पहुँचता है, तो wait को कॉल करने वाला थ्रेड तब तक ब्लॉक हो जाता है जब तक कोई दूसरा थ्रेड signal निष्पादित नहीं करता।
सेमाफोर का मुख्य उद्देश्य क्रिटिकल सेक्शन को कई थ्रेड्स के एक साथ पहुँच से बचाना है। म्यूटेक्स के विपरीत, सेमाफोर को मालिक थ्रेड से बंधन की आवश्यकता नहीं होती, जो इसे समन्वय कार्यों की व्यापक श्रृंखला के लिए उपयुक्त बनाता है।
सेमाफोर की अवधारणा THE ऑपरेटिंग सिस्टम के संदर्भ में उत्पन्न हुई, जो Technische Hogeschool Eindhoven में विकसित किया गया था। डेक्सट्रा ने सेमाफोर को एक गणितीय अमूर्तता के रूप में औपचारिक रूप दिया, किसी भी सिंक्रोनाइज़ेशन प्रिमिटिव को लागू करने के लिए इसकी पर्याप्तता साबित की।
सेमाफोर तंत्र दो परमाणु संक्रियाओं और एक आंतरिक प्रतीक्षा कतार पर आधारित है। जब acquire कॉल किया जाता है, थ्रेड काउंटर मान की जाँच करता है और या तो निष्पादन जारी रखता है या संसाधन मुक्त होने तक ब्लॉक हो जाता है।
सेमाफोर बनाते समय, अनुमति काउंटर का प्रारंभिक मान निर्धारित किया जाता है। प्रत्येक acquire कॉल काउंटर को 1 से घटाता है। यदि इसके बाद काउंटर ऋणात्मक हो जाता है, तो थ्रेड ब्लॉक हो जाता है। release संक्रिया काउंटर को बढ़ाती है और प्रतीक्षा कर रहे थ्रेड्स में से एक को जगाती है।
import java.util.concurrent.Semaphore
val semaphore = Semaphore(3)
fun accessResource() {
semaphore.acquire()
try {
println("${Thread.currentThread().name} काम कर रहा है")
} finally {
semaphore.release()
}
}
जब कोई थ्रेड शून्य काउंटर के साथ acquire कॉल करता है, तो OS उसे सेमाफोर की FIFO कतार में रखता है। थ्रेड BLOCKED अवस्था में चला जाता है, CPU समय का उपभोग नहीं करता। release कॉल के बाद, कतार में पहला थ्रेड RUNNABLE अवस्था में जाता है और संसाधन तक पहुँच प्राप्त करता है।
सिंक्रोनाइज़ेशन सिद्धांत में सेमाफोर के दो मुख्य प्रकार प्रतिष्ठित हैं: बाइनरी और काउंटिंग। प्रकार का चयन संसाधनों तक पहुँच प्रबंधन के विशिष्ट कार्य पर निर्भर करता है।
बाइनरी सेमाफोर केवल मान 0 और 1 लेता है। व्यवहार में, यह म्यूटेक्स जैसा दिखता है, लेकिन स्वामित्व आवश्यकता के बिना — कोई भी थ्रेड release निष्पादित कर सकता है। ऐसे सेमाफोर थ्रेड्स के बीच तैयारी फ्लैग और घटनाओं को लागू करने के लिए सुविधाजनक हैं।
val ready = Semaphore(0)
fun producer() {
Thread.sleep(1000)
ready.release()
}
fun consumer() {
ready.acquire()
println("डेटा तैयार है")
}
काउंटिंग सेमाफोर कोई भी गैर-ऋणात्मक मान ले सकता है। इसका उपयोग समान संसाधनों के पूल को प्रबंधित करने के लिए किया जाता है जहाँ कई इंस्टेंस उपलब्ध हों। उदाहरण के लिए, 5 नेटवर्क कनेक्शन का पूल: प्रत्येक acquire एक कनेक्शन लेता है, release इसे पूल में वापस करता है।
काउंटिंग सेमाफोर बाहरी सेवाओं तक पहुँच की दर सीमित करने और थ्रेड पूल लागू करने के लिए अपरिहार्य हैं। वे मैन्युअल थ्रेड प्रबंधन के बिना समांतरता की डिग्री को सटीक रूप से नियंत्रित करने की अनुमति देते हैं।
| पैरामीटर | बाइनरी सेमाफोर | काउंटिंग सेमाफोर |
|---|---|---|
| सीमा | 0 या 1 | 0 से N तक |
| एक साथ थ्रेड | 1 | N तक |
| उपयोग | सिग्नलिंग, फ्लैग | संसाधन पूल, दर सीमा |
डेवलपर्स अक्सर सेमाफोर और म्यूटेक्स को भ्रमित करते हैं, हालाँकि इनके बीच मूलभूत अंतर हैं। इन अंतरों को समझना किसी प्रोजेक्ट में सही सिंक्रोनाइज़ेशन तंत्र चुनने के लिए अत्यंत महत्वपूर्ण है।
मुख्य अंतर स्वामित्व अवधारणा है। म्यूटेक्स हमेशा जानता है कि किस थ्रेड ने इसे प्राप्त किया है, और केवल वही थ्रेड इसे मुक्त कर सकता है। सेमाफोर का कोई मालिक नहीं है: कोई भी थ्रेड बिना acquire कॉल किए release कॉल कर सकता है। यह म्यूटेक्स को डेटा सुरक्षा के लिए अधिक सुरक्षित और सेमाफोर को समन्वय के लिए अधिक लचीला बनाता है।
व्यवहार में, म्यूटेक्स सामान्य परिदृश्यों के लिए अनुकूलन के कारण सरल पारस्परिक बहिष्करण के लिए तेज़ है। सेमाफोर को काउंटर बनाए रखने के लिए अतिरिक्त ओवरहेड की आवश्यकता होती है। हालाँकि, समांतरता को सीमित करने या उत्पादक-उपभोक्ता पैटर्न को लागू करने के लिए, सेमाफोर अपरिहार्य है।
| विशेषता | Semaphore | Mutex |
|---|---|---|
| स्वामित्व | कोई मालिक नहीं | मालिक है |
| मुक्ति | कोई भी थ्रेड | केवल मालिक थ्रेड |
| काउंटर | 0 से N | बाइनरी |
| उपयोग मामला | समांतरता सीमा और सिग्नलिंग | क्रिटिकल सेक्शन सुरक्षा |
| पुनरावृत्ति | नहीं | हाँ (पुनःप्रवेशी) |
मोबाइल एप्लिकेशन डेवलपमेंट में, Semaphore का उपयोग सीमित संसाधनों तक पहुँच प्रबंधित करने के लिए किया जाता है: नेटवर्क कनेक्शन, फ़ाइलें, डेटाबेस और हार्डवेयर घटक। आधुनिक प्लेटफ़ॉर्म सुविधाजनक अंतर्निहित कार्यान्वयन प्रदान करते हैं।
एक विशिष्ट उपयोग मामला HTTP कनेक्शन पूल है। एक एप्लिकेशन सर्वर को एक साथ 4 से अधिक अनुरोध नहीं भेज सकता क्योंकि प्रदाता का API समांतरता को सीमित करता है। प्रारंभिक मान 4 वाला सेमाफोर सुनिश्चित करता है कि किसी भी भार के तहत, समवर्ती अनुरोधों की संख्या सीमा से अधिक न हो, जबकि अन्य थ्रेड कतार में प्रतीक्षा करें।
सेमाफोर के बिना, उपयोगकर्ता गतिविधि में तेज वृद्धि सर्वर बुनियादी ढाँचे पर अचानक अधिभार का कारण बन सकती है, जिससे टाइमआउट और 429 Too Many Requests त्रुटियाँ हो सकती हैं। Semaphore एक फ़्यूज़ की तरह काम करता है, सक्रिय थ्रेड्स की संख्या की परवाह किए बिना सख्ती से निर्दिष्ट संख्या में समवर्ती कॉल की अनुमति देता है।
Android java.util.concurrent पैकेज से Semaphore क्लास प्रदान करता है। आइए सर्वर अधिभार को रोकने के लिए समवर्ती नेटवर्क अनुरोधों को दो थ्रेड्स तक सीमित करने का उदाहरण देखें।
class ApiClient {
private val throttle = Semaphore(2)
suspend fun fetch(url: String): Result {
throttle.acquire()
return try {
httpGet(url)
} finally {
throttle.release()
}
}
}
iOS में, GCD से DispatchSemaphore उसी कार्य को हल करता है। डेवलपर्स इसका उपयोग मुख्य थ्रेड को ब्लॉक किए बिना अतुल्यकालिक कोड में संसाधनों तक पहुँच को सिंक्रोनाइज़ करने के लिए करते हैं।
let semaphore = DispatchSemaphore(value: 3)
func processBatch(_ items: [UIImage]) {
for img in items {
semaphore.wait()
DispatchQueue.global().async {
applyFilter(to: img)
semaphore.signal()
}
}
}
सबसे आम गलती अपवाद होने पर भूला हुआ release है। यदि कोई थ्रेड release कॉल करने से पहले त्रुटि के साथ समाप्त होता है, तो सेमाफोर अन्य थ्रेड्स के लिए स्थायी रूप से ब्लॉक रहता है। गारंटीकृत मुक्ति के लिए try/finally या defer का उपयोग करें। दूसरी समस्या विभिन्न थ्रेड्स द्वारा अलग-अलग क्रम में कई सेमाफोर प्राप्त करने पर deadlock है।
सेमाफोर का उपयोग न केवल डेटा सुरक्षा के लिए, बल्कि जटिल मल्टीथ्रेडेड परिदृश्यों में थ्रेड समन्वय के लिए भी किया जाता है। सामान्य पैटर्न जानने से विकास में तेज़ी आती है और सिंक्रोनाइज़ेशन त्रुटियों की संभावना कम होती है।
वास्तविक प्रोजेक्ट्स में सेमाफोर के उपयोग के कई सिद्ध पैटर्न हैं। उन्हें जानने से सामान्य गलतियों से बचने और विश्वसनीय मल्टीथ्रेडेड सिस्टम बनाने में मदद मिलती है।
प्रारंभिक मान N और टाइमर के माध्यम से आवधिक release वाला सेमाफोर API अनुरोधों की दर सीमा लागू करता है। उदाहरण के लिए, सेवा प्रति सेकंड 10 अनुरोधों की अनुमति देती है: सेमाफोर 10 से शुरू होता है, प्रत्येक अनुरोध काउंटर घटाता है, और एक अलग TimerTask हर सेकंड काउंटर को उसके प्रारंभिक मान पर लौटाता है। यह एप्लिकेशन और सर्वर दोनों को अधिभार से बचाता है।
क्लासिक उत्पादक-उपभोक्ता समस्या में, दो सेमाफोर एक बफ़र का प्रबंधन करते हैं: empty (लेखन अनुमति) और full (पढ़ने की अनुमति)। उत्पादक empty पर acquire और full पर release कॉल करता है, जबकि उपभोक्ता इसके विपरीत करता है। यह योजना सुनिश्चित करती है कि उपभोक्ता कभी खाली बफ़र न पढ़े और उत्पादक कभी इसे ओवरफ़्लो न करे।
यही योजना ऑपरेटिंग सिस्टम में बाउंडेड बफ़र — निश्चित आकार के रिंग बफ़र का आधार है। मोबाइल एप्लिकेशन में, पैटर्न का उपयोग छवियों, वीडियो फ़ाइलों और विश्लेषणात्मक घटनाओं की कतारों को संसाधित करने के लिए किया जाता है।
सेमाफोर का उपयोग पृष्ठभूमि सेवाओं में नेटवर्क कॉल को थ्रॉटल करने के लिए सफलतापूर्वक किया जाता है। उदाहरण के लिए, एक विश्लेषण एप्लिकेशन सर्वर को ईवेंट पैकेट भेजता है। पीक लोड (ऐप लॉन्च, ऑफलाइन के बाद सिंक्रोनाइज़ेशन) के दौरान समवर्ती थ्रेड्स को सीमित किए बिना, एक साथ अनुरोधों की संख्या सर्वर सीमा से अधिक हो सकती है। प्रारंभिक मान 3 वाला सेमाफोर सुचारू भेजना सुनिश्चित करता है और सर्वर-साइड ब्लॉकिंग को रोकता है।
अक्सर पूछे जाने वाले प्रश्न
Semaphore केवल एक काउंटर नहीं है, बल्कि परमाणु संक्रियाओं और प्रतीक्षा कतार वाला एक सिंक्रोनाइज़ेशन प्रिमिटिव है। सामान्य काउंटर थ्रेड को ब्लॉक नहीं करता और कई थ्रेड्स के एक साथ पहुँच पर परमाणु वृद्धि की गारंटी नहीं देता।
हाँ, deadlock संभव है जब विभिन्न थ्रेड्स द्वारा अलग-अलग क्रम में कई सेमाफोर प्राप्त किए जाते हैं। उदाहरण के लिए, थ्रेड A S1, फिर S2 प्राप्त करता है, जबकि थ्रेड B S2, फिर S1 प्राप्त करता है। प्रोजेक्ट में सभी सेमाफोर के लिए एकल अधिग्रहण क्रम निर्धारित करें।
थ्रेड ब्लॉक हो जाता है और प्रतीक्षा अवस्था में चला जाता है। यह तब तक CPU समय का उपभोग नहीं करता जब तक कोई दूसरा थ्रेड release कॉल न करे। Java में, यह BLOCKED अवस्था है; Swift में, थ्रेड GCD द्वारा निलंबित कर दिया जाता है।
मुख्य अंतर स्वामित्व है। Mutex को केवल मालिक थ्रेड द्वारा मुक्त किया जा सकता है। Binary Semaphore को किसी भी थ्रेड द्वारा मुक्त किया जा सकता है, जो थ्रेड्स के बीच सिग्नलिंग के लिए सुविधाजनक है लेकिन डेटा अखंडता की सुरक्षा के लिए कम सुरक्षित है।
प्रारंभिक मान परिदृश्य पर निर्भर करता है। एकल संसाधन की सुरक्षा के लिए — 1. N कनेक्शन के पूल के लिए — N. थ्रेड्स के बीच सिग्नलिंग के लिए, 0 का उपयोग करें ताकि उपभोक्ता थ्रेड उत्पादक से संकेत की प्रतीक्षा करे।
सारांश
हम एक मोबाइल एप्लिकेशन टर्नकी विकसित करेंगे
IT Sectr 2017 से स्टार्टअप और व्यवसायों के लिए iOS और Android एप्लिकेशन बनाता है। हम आपको सलाह देंगे और सर्वोत्तम समाधान प्रस्तावित करेंगे।
यह भी पढ़ें