مقدمه
در درسهای قبلی با کلاس «Semaphore» در ماژول «threading» آشنا شدیم. ماژول «multiprocessing» هم نسخهی مشابهی از همین کلاس را در اختیار ما قرار میدهد تا بتوانیم تعداد پردازشهایی که همزمان به یک منبع مشترک دسترسی دارند را محدود کنیم.
ایجاد یک Semaphore
برای ساخت یک نمونه از «Semaphore» در ماژول «multiprocessing» کافی است یک عدد بهعنوان «value» به آن بدهیم؛ این عدد نشان میدهد حداکثر چند پردازش میتوانند همزمان وارد بخش محافظتشده شوند.
sem = multiprocessing.Semaphore(2)
این شیء باید بین پردازشهای فرزند به اشتراک گذاشته شود، یعنی باید آن را بهعنوان آرگومان به تابعی که در پردازش جدید اجرا میشود پاس بدهیم.
مثال: محدود کردن دسترسی همزمان به یک منبع
فرض کنید چند پردازش میخواهند از یک منبع محدود (مثلاً یک فایل یا یک اتصال شبکه) استفاده کنند، ولی این منبع تنها ظرفیت دو دسترسی همزمان را دارد. با «Semaphore» میتوانیم این محدودیت را اعمال کنیم.
def access_resource(sem, worker_id):
with sem:
print(f'worker-{worker_id}: entered')
print(f'worker-{worker_id}: leaving')
if __name__ == '__main__':
sem = multiprocessing.Semaphore(2)
workers = []
for i in range(5):
p = multiprocessing.Process(target=access_resource, args=(sem, i))
workers.append(p)
p.start()
for p in workers:
p.join()
در این مثال پنج پردازش ساخته میشوند، اما در هر لحظه فقط دو تا از آنها میتوانند وارد بلوک «with sem» شوند. بقیه باید منتظر بمانند تا یکی از دو جای آزاد، خالی شود.
تفاوت با BoundedSemaphore
مانند نسخهی «threading»، کلاس «multiprocessing.BoundedSemaphore» هم وجود دارد که اگر متد «release» بیشتر از حد اولیه فراخوانی شود، یک استثنا ایجاد میکند. این موضوع به پیدا کردن اشتباهات برنامهنویسی کمک میکند.
جمعبندی
«Semaphore» در ماژول «multiprocessing» همان مفهوم آشنای محدودسازی دسترسی همزمان را به دنیای پردازشها میآورد. استفادهی آن دقیقاً مانند نسخهی «threading» است، با این تفاوت که شیء باید بین پردازشها به اشتراک گذاشته شود.