راه
۴ کیو· سختپیشرفتهPYTHON۴ ثانیه · ۲۵۶ مگابایت

مث‌جاوا (Synchronized Decorator)

در این تمرین، از شما خواسته می‌شود یک دکوراتور بنویسید که عملکردی مشابه کلیدواژه‌ی synchronized در زبان جاوا داشته باشد.

این دکوراتور باید به گونه‌ای پیاده‌سازی شود که وقتی بر روی یک تابع قرار می‌گیرد، تضمین کند که در هر لحظه، تنها یک ترد اجازه‌ی اجرای آن تابع را دارد.

  • اگر چندین ترد به‌صورت هم‌زمان تلاش کنند تا تابع دکوره‌شده را فراخوانی کنند، سایر ترد‌ها تا زمان اتمام اجرای تابع توسط ترد جاری، در حالت انتظار باقی خواهند ماند.
  • اجرای ترد‌های منتظر به‌صورت ترتیبی (بر اساس زمان ورود به صف انتظار) انجام خواهد شد.
  • برای پیاده‌سازی این همگام‌سازی، شما باید از شیء Lock استفاده کنید.

⚠️ نکات مهم

  • دکوراتور شما باید به گونه‌ای نوشته شود که بتواند بر روی هر تابعی، صرف نظر از تعداد و نوع آرگومان‌های ورودی و مقادیر خروجی آن، اعمال شود و عملکرد صحیح داشته باشد.
  • سازوکار اصلی همگام‌سازی در دکوراتور شما حتماً باید با استفاده از یک شیء Lock مدیریت شود.

🎯 مثال

python
from threading import Thread, Lock


def synchronized(func):
    ...

a = 0

@synchronized
def f():
    global a
    for i in range(300000):
        a += 1

t = Thread(target=f)
s = Thread(target=f)

print("Starting threads...")

t.start()
s.start()

t.join()
s.join()

print("Threads finished.")

print(f'{a = }')

خروجی:

text
Starting threads...
Threads finished.
a = 600000
  • دو ترد (t و s) ایجاد شده‌اند که هر دو تابع f را اجرا می‌کنند.
  • تابع f یک متغیر سراسری به نام a را ۳۰۰۰۰۰ بار افزایش می‌دهد.
  • با استفاده از دکوراتور @synchronized که شما پیاده‌سازی می‌کنید، اجرای تابع f همگام‌سازی می‌شود.
  • مقدار نهایی متغیر a دقیقاً برابر با حاصل جمع افزایش‌های هر دو ترد (۶۰۰۰۰۰) خواهد بود.

💡 راهنمایی

ساختار کلی دکوراتور @synchronized به این شکل است:

python
def synchronized(func):
    lock = Lock()

    def wrapper(*args, **kwargs):
        with lock:
            return func(*args, **kwargs)

    return wrapper
  • دکوراتور @synchronized یک Lock ایجاد می‌کند که بین تمام فراخوانی‌های تابع مشترک است.
  • تابع wrapper با استفاده از with قفل را می‌گیرد و سپس تابع اصلی را اجرا می‌کند.
  • استفاده از with تضمین می‌کند که قفل پس از اجرای تابع (حتی در صورت بروز خطا) آزاد شود.
  • استفاده از *args و **kwargs باعث می‌شود دکوراتور بر روی هر تابعی با هر تعداد آرگومان کار کند.
  • مقدار بازگشتی تابع اصلی با return برگردانده می‌شود.
  • در این تمرین، نیازی به دریافت ورودی از کاربر یا چاپ خروجی در کنسول ندارید.
  • شما فقط باید دکوراتور @synchronized را پیاده‌سازی کنید.
  • دکوراتور شما باید با استفاده از Lock همگام‌سازی را انجام دهد.
  • دکوراتور باید بر روی هر نوع تابعی (با هر تعداد آرگومان) کار کند.
  • نیازی به فراخوانی دکوراتور در کد ارسالی نیست. سیستم به‌طور خودکار آن را تست خواهد کرد.

✅ این تمرین را در ویرایشگر خود پیاده‌سازی کنید. به یاد داشته باشید که دکوراتور @synchronized باید با استفاده از Lock همگام‌سازی را انجام دهد.

نمونه‌ها

ورودی نمونه ۱
import time
from threading import Thread

counter = {"n": 0}

@synchronized
def bump():
    for _ in range(200):
        value = counter["n"]
        time.sleep(0)
        counter["n"] = value + 1

threads = [Thread(target=bump) for _ in range(4)]
for t in threads:
    t.start()
for t in threads:
    t.join()
counter["n"]
خروجی نمونه ۱
800
ورودی نمونه ۲
import time
from threading import Thread

inside = []
overlaps = []

@synchronized
def work():
    inside.append(1)
    if len(inside) > 1:
        overlaps.append(True)
    time.sleep(0.01)
    inside.pop()

threads = [Thread(target=work) for _ in range(5)]
for t in threads:
    t.start()
for t in threads:
    t.join()
overlaps
خروجی نمونه ۲
[]
ورودی نمونه ۳
@synchronized
def add(a, b=0):
    return a + b

add(2, 3)
add(5, b=7)
خروجی نمونه ۳
5
12
ورودی نمونه ۴
@synchronized
def maybe(fail):
    if fail:
        raise ValueError("boom")
    return "ok"

try:
    maybe(True)
except ValueError as error:
    print(error)
maybe(False)
خروجی نمونه ۴
boom
'ok'

برای نوشتن پاسخ و دریافت امتیاز، ابتدا وارد حساب شوید.

ورود