پاسخ تمرین ۱
Condition برای هماهنگیِ «منتظرماندن تا وقوع یک شرط» بین تردها (الگوی تولیدکننده/مصرفکننده) استفاده میشود. نسبت به busy-waiting بهتر است چون ترد منتظر میخوابد و CPU مصرف نمیکند؛ فقط وقتی شرط برقرار شد با notify بیدار میشود.
پاسخ تمرین ۲
در حالت if، ترد فقط یکبار شرط را بررسی میکند و اگر بیدارشدنش بهاشتباه (Spurious Wakeup) یا بهخاطر نوتیفای ترد دیگری باشد، بدون اینکه شرط واقعی برقرار شده باشد ادامه میدهد — همان اشکال «گارسون سارق». while باعث میشود ترد بعد از هر بار بیدارشدن دوباره شرط را بررسی کند و فقط وقتی واقعاً برقرار است از حلقه خارج شود؛ در نتیجه هم Spurious Wakeup و هم از دست رفتن Notify برطرف میشود.
پاسخ تمرین ۳
notify() فقط یکی از تردهای منتظر را (به انتخاب سیستم) بیدار میکند، اما notify_all() همه را. وقتی فقط یک منبع برای مصرف آماده شده، notify() کافی است؛ اما اگر وضعیتِ مشترک تغییر کرده و چند ترد باید دوباره شرط را بررسی کنند، از notify_all() استفاده کنید تا هیچ تردی برای همیشه منتظر نماند.
پاسخ تمرین ۴
main_lock از دادههای عمومی انبار محافظت میکند و item_condition مخصوص هماهنگکردن تردهایی است که منتظر رسیدن یک کالای خاصاند. اگر فقط یک قفل مشترک به کار میرفت، تردی که روی Condition منتظر است باید همان قفل عمومی را هم نگه میداشت (چون wait فقط قفلِ خودِ Condition را آزاد میکند)؛ این میتوانست به Deadlock منجر شود، چون تولیدکننده برای ثبت کالا به قفل عمومی نیاز دارد اما آن در دست مصرفکنندهی خوابیده مانده است.
پاسخ تمرین ۵
وقتی تردی در حالی که قفل Condition را در اختیار دارد یک تأخیر طولانی (با time.sleep) اجرا کند، همهی تردهای منتظرِ همان قفل هم باید همان مدت صبر کنند، حتی اگر کاری برای انجامدادن نداشته باشند. این کار همزمانی را بهشدت کم میکند و مزیت چند ترد را از بین میبرد. قاعده: فقط بخشی را که به منبع مشترک دست میزند داخل قفل بگذارید و کارهای سنگین را بیرون از آن انجام دهید.
پاسخ تمرین ۶
import threading
import time
condition = threading.Condition()
is_ready = False
def waiter():
with condition:
while not is_ready:
condition.wait()
print("[Waiter] order is ready, serving it!")
def chef():
global is_ready
time.sleep(1)
with condition:
is_ready = True
print("[Chef] done! notifying...")
condition.notify()
threading.Thread(target=waiter).start()
threading.Thread(target=chef).start()پاسخ تمرین ۷
import threading
import time
condition = threading.Condition()
job_queue = []
def worker():
while True:
with condition:
while not job_queue:
condition.wait()
job = job_queue.pop(0)
if job is None: # shutdown signal
print("worker stopping")
break
print("processing", job)
time.sleep(0.2)
def dispatcher(jobs):
for j in jobs:
with condition:
job_queue.append(j)
condition.notify()
time.sleep(0.1)
with condition:
job_queue.append(None) # tell the worker to stop
condition.notify()
w = threading.Thread(target=worker)
d = threading.Thread(target=dispatcher, args=(['a', 'b', 'c'],))
w.start(); d.start()
w.join(); d.join()راهحل جایگزین: در عمل معمولاً بهجای پیادهسازی دستی از queue.Queue استفاده میشود که در پشتصحنه بر پایهی Condition ساخته شده است.
پاسخ تمرین ۸
این متدها باید وقتی فراخوانی شوند که قفلِ Condition در دست ماست؛ یعنی داخل بلاک with condition: یا بین acquire() و release(). اگر بیرون از قفل صدا زده شوند، RuntimeError رخ میدهد.