پاسخ تمرین ۱
هر ترد در پایتون حافظهی قابلتوجهی (معمولاً چند مگابایت برای پشته) مصرف میکند و سیستمعامل هم نمیتواند بیستهزار ترد را بهخوبی مدیریت کند. علاوه بر این، هماهنگی بین اینهمه ترد و جمعآوری نتایجشان خودش یک مشکل بزرگ جدید ایجاد میکند.
پاسخ تمرین ۲
همروال تابعی است که میتواند در میانهی اجرا متوقف شود و بعداً از همان نقطه، بدون از دست دادن وضعیتش، ادامه یابد. در داستان صبحانه «شما» نقش حلقهی رویداد (event loop) را بازی میکنید: ناظر و برنامهریزی که سفارشها را شروع میکند، میخوابد و با رسیدن هر سفارش بیدار میشود. (خودِ «دستورالعملِ زنگزدن و سفارشدادن و منتظرماندن» همان coroutine است.)
پاسخ تمرین ۳
- coroutine: تابعی که میتواند وسط اجرا مکث و بعداً ادامه یابد (با
async def). - event loop: ناظری که کارهای آماده را اجرا و کارهای منتظر را کنار میگذارد.
- Task: همروالی که در حلقه ثبت شده تا اجرا شود.
- awaiting: اعلام «نمیتوانم ادامه دهم تا X فراهم شود» و بازگرداندن کنترل به حلقه.
پاسخ تمرین ۴
زنگ در معادل یک رویداد ورودی/خروجی است (مثل رسیدن داده روی یک سوکت). همین رویداد سیستمعامل را بیدار میکند و سیستمعامل هم حلقهی رویداد را. وقتی حلقه بیدار شد، Task مربوطه را از مجموعهی «در انتظار» به صف «آماده» منتقل میکند تا دوباره اجرا شود.
پاسخ تمرین ۵
حلقه فقط وقتی میخوابد که هیچ Task آمادهای در صف نباشد؛ در آن حالت به سیستمعامل میگوید تا رسیدن یک رویداد I/O او را بیدار نکند و در تماسی مثل epoll_wait منتظر میماند. اگر حتی یک Task آماده باشد، حلقه بدون تأخیر آن را اجرا میکند و اصلاً نمیخوابد. این خواب هیچ CPU مصرف نمیکند.
پاسخ تمرین ۶
time.sleep کل ترد و بنابراین کل حلقهی رویداد را بلاک میکند و مانع پیشرفت بقیهی کارها میشود. بهجای آن از await asyncio.sleep(...) استفاده میکنیم که غیرمسدودکننده است و کنترل را به حلقه بازمیگرداند.
پاسخ تمرین ۷
import asyncio
import time
async def order(item, seconds):
print(f"ordering {item}...")
await asyncio.sleep(seconds)
print(f"{item} delivered")
return item
async def main():
start = time.time()
results = await asyncio.gather(
order("bread", 2),
order("milk", 1),
order("juice", 3),
)
print(results, "in %.1fs" % (time.time() - start))
asyncio.run(main()) # ~3s (the longest), not 6sپاسخ تمرین ۸
import asyncio
import time
async def health_check(server_id):
await asyncio.sleep(1)
return (server_id, "ok")
async def main():
start = time.time()
tasks = [asyncio.create_task(health_check(i)) for i in range(200)]
results = await asyncio.gather(*tasks)
print(f"checked {len(results)} servers in %.2fs" % (time.time() - start))
asyncio.run(main()) # ~1s