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 ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔
مزید پڑھیں