ماژول thread_ – قفل کردن منابع مشترک

Please login to bookmark Close

چرا باید قفل گذاری کنیم؟

منابع یک process بین thread هایش به اشتراک گذاشته می‌شود. در صورتی ترد ها بخواهند در یک لحظه از یک منابع مشترک استفاده کنند مشکلاتی نظیر  Race Condition یا خطای مقابله ای  رخ می‌دهد. با قفل‌گذاری می‌توان مشخص کرد که یک منبع اشتراکی در هر لحظه توسط کدام ترد استفاده شود.

به عملیات قفل کردن منابع مشترک، Lock کردن هم گفته می‌شود.

مثال – سرویس بهداشتی عمومی

استفاده از یک سرویس بهداشتی عمومی که درب آن قفل ندارد چه حسی دارد؟ هر لحظه ممکن است آن اتفاقی که نباید، رخ دهد. یک منبع مشترک مانند یک سرویس بهداشتی عمومی است و افرادی که می‌خواهند از آن استفاده کنند مانند ترد هایی هستند که از آن منبع مشترک استفاده می‌کنند.

همانطور که برای حل مشکل سرویس بهداشتی کافیست برای درب آن قفل بگذاریم و آن را قفل کنیم، در مولتی تردینگ نیز برای حل این مشکل از عملیات قفل گذاری استفاده می‌کنیم. به عملیات قفل‌گذاری در اصطلاح فنی‌تر، lock کردن گفته می‌شود.برای برنامه‌نویسی مولتی ترد در پایتون سه ماژول زیر وجود دارد:

قفل گذاری در ماژول _thread

قفل گذاری شامل سه مرحله است:

  • اول: ایجاد قفل
  • دوم: بستن قفل
  • سوم: باز کردن قفل
import _thread as thread
import time

# شمارنده مشترک
counter = 0

# قفل برای همگام‌سازی دسترسی به متغیر مشترک
lock = thread.allocate_lock()

# تابع افزایش شمارنده
def add(thread_id):
    global counter
    for _ in range(100):
        # قفل گرفتن برای دسترسی به متغیر مشترک
        lock.acquire()
        temp = counter
        temp += 1
        time.sleep(0.01)  # اضافه کردن یک خواب کوتاه برای تشدید شرایط رقابتی
        counter = temp
        print(f"Thread {thread_id}: {counter}")
        # قفل رها کردن بعد از استفاده
        lock.release()

def subtract(thread_id):
    global counter
    for _ in range(100):
        # قفل گرفتن برای دسترسی به متغیر مشترک
        lock.acquire()
        temp = counter
        temp -= 1
        time.sleep(0.01)  # اضافه کردن یک خواب کوتاه برای تشدید شرایط رقابتی
        counter = temp
        print(f"Thread {thread_id}: {counter}")
        # قفل رها کردن بعد از استفاده
        lock.release()

# ایجاد چندین نخ
thread.start_new_thread(add, (1,))
thread.start_new_thread(subtract, (2,))

# منتظر ماندن برای پایان نخ‌ها
time.sleep(5)

print(f"Final counter value: {counter}")

در تکه کد بالا:

  • خط ۸: با استفاده از thread.allocate_lock قفل ساخته می‌شود.
  • خط ۱۵ و ۲۸: با استفاده از acquire قفل بسته می‌شود.
  • خط ۲۲ و ۳۵: با استفاده از release قفل باز می‌شود.

اگر یک ترد، هر کدام از acquire ها را اجرا کند، ترد های دیگر زمانی که به acquire ها برسند متوقف می‌شوند تا آن ترد با اجرای release، قفل را باز و منابع مشترک را آزاد کند.

بدین ترتیب تمام کد هایی که بین acquire و release نوشته می‌شوند در یک لحظه فقط توسط یک ترد اجرا می‌شوند.

استفاده از with برای قفل کردن

به جای استفاده از acquire و release می‌توان از دستور with برای قفل کردن استفاده کرد. این کار علاوه بر افزایش خوانایی کد، موجب کاهش احتمال بروز خطای Dead Lock می‌شود.

import time
import _thread as thread

# شمارنده مشترک
counter = 0

# قفل برای همگام‌سازی دسترسی به متغیر مشترک
lock = thread.allocate_lock()

# تابع افزایش شمارنده
def add(thread_id):
    global counter
    for _ in range(100):
        # قفل گرفتن برای دسترسی به متغیر مشترک با استفاده از with
        with lock:
            temp = counter
            temp += 1
            time.sleep(0.01)  # اضافه کردن یک خواب کوتاه برای تشدید شرایط رقابتی
            counter = temp
            print(f"Thread {thread_id}: {counter}")

# تابع کاهش شمارنده
def subtract(thread_id):
    global counter
    for _ in range(100):
        # قفل گرفتن برای دسترسی به متغیر مشترک با استفاده از with
        with lock:
            temp = counter
            temp -= 1
            time.sleep(0.01)  # اضافه کردن یک خواب کوتاه برای تشدید شرایط رقابتی
            counter = temp
            print(f"Thread {thread_id}: {counter}")

# ایجاد چندین نخ
thread.start_new_thread(add, (1,))
thread.start_new_thread(subtract, (2,))

# منتظر ماندن برای پایان نخ‌ها
time.sleep(5)

print(f"Final counter value: {counter}")

خلاصه

  • هنگامی که منابع مشترک بین ترد ها وجود دارد، برای جلوگیری از رخ دادن مشکلات الگوریتمی ناشی از Race Condition، ترد ها را قفل می‌کنیم.
  • برای افزایش خوانایی کد و کاهش خطای Dead Lock استفاده از with به جای acquire و release پیشنهاد می‌شود.
Please login to bookmark Close
پیشرفت شما در «دوره آموزش کانکارنسی در پایتون» (16%)
نظرات

دیدگاهتان را بنویسید

16%
پیشرفت
فهرست مطالب

تمرین

این قسمت تمرین ندارد!

پاسخ تمرین ها

هنوز برای تمرین‌های این قسمت پاسخی ثبت نشده است!

اشتراک گذاری

چرا بهتره از فیلترشکن استفاده کنید؟

من همه ویدئو ها و پادکست های کُدباز رو توی یوتیوب و ساندکلود و پلتفرم هایی آپلود می‌کنم که اغلب فیلتر هستند.

اغلب آموزش‌ها ویدئو و پادکست دارند. پس اگر می‌خواهید از محتوای سایت بیشترین استفاده رو ببرید نیاز به فیلتر شکن دارید.

توجه داشته باشید که برای خرید از فروشگاه بهتره فیلتر شکن رو خاموش کنید.

تنظیمات

انتخاب زبان
تغییر تم