مقدمه
کلاس Event که در درس قبل دیدیم، فقط یک سیگنال ساده مخابره میکند و اطلاعی از وضعیت دادههای مشترک ندارد. اما گاهی یک Task باید منتظر بماند تا یک شرط خاص دربارهٔ دادههای مشترک برقرار شود؛ مثلاً «منتظر بمان تا صف خالی نباشد». کلاس Condition در asyncio دقیقاً برای این منظور طراحی شده و یک Lock را با قابلیت wait/notify ترکیب میکند.
ساخت یک Condition
با فراخوانی asyncio.Condition() یک شیء Condition ساخته میشود که بهصورت داخلی یک asyncio.Lock نیز دارد. تمام عملیات روی دادههای مشترک باید درون async with condition انجام شود تا از دسترسی همزمان و ناخواسته جلوگیری شود.
متدهای اصلی کلاس Condition
- await condition.wait(): «باید درون async with صدا زده شود؛ Lock را موقتاً آزاد میکند، منتظر notify میماند و سپس دوباره Lock را میگیرد.»
- await condition.wait_for(predicate): «بهصورت تکرارشونده wait() را صدا میزند تا زمانی که تابع predicate مقدار True برگرداند.»
- condition.notify(n=1): «یک یا چند Task منتظر را بیدار میکند (پیشفرض فقط یکی).»
- condition.notify_all(): «همهٔ Taskهای منتظر را بیدار میکند.»
مثال: Producer-Consumer با شرط مشخص
در این مثال، Consumer منتظر میماند تا لیست مشترک items حداقل یک آیتم داشته باشد؛ این شرط دقیقاً همان چیزی است که wait_for بررسی میکند.
import asyncio
import random
async def producer(condition, items):
for i in range(5):
delay = random.uniform(0.2, 0.6)
await asyncio.sleep(delay)
async with condition:
items.append(f'item-{i}')
print(f'producer: تولید شد -> item-{i}')
condition.notify()
async def consumer(condition, items):
while True:
async with condition:
await condition.wait_for(lambda: len(items) > 0)
item = items.pop(0)
print(f'consumer: مصرف شد -> {item}')
if item == 'item-4':
break
async def main():
condition = asyncio.Condition()
items = []
await asyncio.gather(
producer(condition, items),
consumer(condition, items)
)
asyncio.run(main())نکتهٔ مهم این است که condition.notify() باید درون همان بلوک async with صدا زده شود که دادههای مشترک تغییر کردهاند؛ در غیر این صورت ممکن است Task منتظر هیچگاه از تغییر باخبر نشود.
جمعبندی
کلاس Condition برای سناریوهایی مناسب است که Taskها باید منتظر برقراری یک شرط مشخص دربارهٔ دادههای مشترک بمانند، نه فقط یک سیگنال ساده. در درس بعدی با متد shield آشنا میشویم که به ما اجازه میدهد بخشی از کد را در برابر لغو شدن (Cancellation) محافظت کنیم.